Real-Life Case of the Command Design Pattern

Поделиться
HTML-код
  • Опубликовано: 27 дек 2024

Комментарии • 94

  • @ArjanCodes
    @ArjanCodes  3 года назад +8

    The first 1,000 people to use this link will get a 1 month free trial of Skillshare! - skl.sh/arjancodes11211

    • @havenisse2009
      @havenisse2009 3 года назад +3

      Unfortunately it is not possible to get the free month without giving skillshare a credit card up front. Something they don't advertise until they have your email. If only they could be honest, give a free monht, then force people to add a credit card when trial was over... But now, It's a no thank you from me. Not your fault though.

    • @ArjanCodes
      @ArjanCodes  3 года назад +4

      Yeah... that's standard practice for many subscription services nowadays. If you don't want to give companies access to your credit card, there are services (like privacy.com) that offer temporary credit card numbers you can destroy at any point in time. I haven't used these services myself, but I might in the future as I share some of your concerns.

  • @XRay777
    @XRay777 3 года назад +31

    Small side note: What Arjan did at the end with the Batch class is actually another pattern that often goes by the name Composite.
    It is a way to package up primitve classes into groupings that implement the same interface. Consequently, calling code can treat individual parts or groups of parts uniformly.
    I like this example here a lot, since it is great at highlighting how combining patterns unlocks their true potential. 👍

  • @wimdegroot6815
    @wimdegroot6815 3 года назад +11

    The command pattern is one of the most satisfying patterns in my opinion.

  • @syberen
    @syberen 3 года назад +10

    Really like your channel. The only feedback I have is that on Friday end of the day, when the new videos drop, my brain is already fried from coding all week 😅

    • @ArjanCodes
      @ArjanCodes  3 года назад +1

      Haha, I know the feeling :).

    • @virtualraider
      @virtualraider 3 года назад +4

      I'm in Australia, so I watch it in my morning with breakfast. My wife says that @ArjanCodes has a relaxing voice so she doesn't mind me nerding it out 😅

    • @MrAlFuture
      @MrAlFuture 3 года назад +2

      @@virtualraider yep I concur. ArjanCodes and coffee are a good Saturday morning here in Tasmania.

  • @biermeester
    @biermeester 3 года назад +16

    One nice feature in Python, that not many people seem to know, is that you can add underscores to numbers to improve readability, eg: account1.withdraw(150_000)

    • @ArjanCodes
      @ArjanCodes  3 года назад +4

      Indeed! I always forget to use that. It would have been quite useful in this example.

  • @MrAlFuture
    @MrAlFuture 3 года назад +7

    This is a wonderful series (so far!). I really appreciate the non-trivial yet straightforward scenarios you're using to demonstrate the pattern. Thanks, Arjan!

    • @ArjanCodes
      @ArjanCodes  3 года назад

      Glad to hear you like it, thanks!

  • @red_cape.
    @red_cape. 3 года назад +2

    "Thanks" for the tips Arjan, now I have to rewrite my personal finance desktop app program /s . But now in all seriousness, man your videos are great love the power of this command pattern, I just wish that I've known it earlier, the undo functionality was already planned in my roadmap, but seeing this now I think it should be cooked into the system from the beginning. I feel like a children in a candy shop, each new video is like "Hey I want that!".

  • @BiologyIsHot
    @BiologyIsHot 3 года назад +3

    Me who just implemented a messy text-based command system for a project and has never heard of this pattern... 😅😅 time to watch

  • @mykhailo_klym
    @mykhailo_klym 2 года назад

    Perfect, what I love is that you give some decent real life examples.

  • @manumoreno99
    @manumoreno99 3 года назад +2

    Great video Arjan! I've watched most of your videos. They are all very good. I like the way your present your ideas. I'm in the process of creating my own portfolio management system and you're giving me some good ideas -- especially with this video and the follow-up one where you'll be using transactions as the basis for managing state.

  • @bab333bab
    @bab333bab 3 года назад

    Very well done. Implemented that pattern years ago by myself- now after watching you video I don't remember why I struggled with it ;-).

    • @ArjanCodes
      @ArjanCodes  3 года назад

      Thank you! Glad you liked it!

  • @n.nikolaev
    @n.nikolaev 2 года назад +1

    Arjan, once again you did an awesome job! The pattern series is truly great and useful. May I, however, as a junior programmer, suggest that you use UML class diagrams to elaborate more on the patterns' concepts and the particular examples that you present. It will be very helpful to inexperienced coders because very often these patterns include more than two layers of abstraction and it gets a little hard to comprehend the entire idea just by looking at you going through multiple modules and classes.
    I really appreciate your work!

  • @siddsp02
    @siddsp02 3 года назад +1

    I think this can be simplified by just storing commands in two lists or dicts which can act like stacks anyways. When you want to undo, the second stack's last item is whatever is popped from the first. When you want to redo, the first stack's last item is what is popped from the second.
    Balance can be calculated by having some sort of dictionary where results are cached, so lookup isn't expensive.

  • @oreychandan9229
    @oreychandan9229 3 года назад +1

    I love your videos! I get to improve my code quality so much and make my development experience more fun!. Thank you for doing what you do.

    • @ArjanCodes
      @ArjanCodes  3 года назад

      Glad to hear the videos are helpful!

  • @deez_gainz
    @deez_gainz 3 года назад +1

    Hi Arjan, thanks a lot for your design pattern videos, really learning a lot. If you get short on video ideas how about this one "Most useful design patterns for creating your own REST API".

  • @cheebadigga4092
    @cheebadigga4092 2 года назад

    Damn this is really powerful! Thanks for sharing!!

    • @ArjanCodes
      @ArjanCodes  2 года назад +1

      You’re welcome Cheeba!

  • @jeffporzio1382
    @jeffporzio1382 3 года назад +3

    I always mix up the command pattern with the strategy pattern. Can you make a video comparing the two like you've done with other concepts?

    • @qwerty11111122
      @qwerty11111122 3 года назад

      It looks like strategy has a time delay between setting and usage and commands are used immediately. If a dog is set to happy strategy, then later, commanding it to speak makes it bark. If the dog is set to scared strategy, then commanding it to speak makes it go "rut's sat, raggy?"

    • @fennecbesixdouze1794
      @fennecbesixdouze1794 3 года назад +2

      Just because the have similar sounding names or what? They really aren't similar at all.
      In the command pattern, you have a bunch of different operations that you are frequently executing throughout the application. Usually these commands have completely different interfaces. You take all these different operations, rewrite them so that they are invoked using objects that all implement a common "command" interface with an "execute" method. Each individual command still has to be created with its own interface, but instead of invoking the code directly you can at some later time call "execute" on the command object to run the code. Then rather than calling all the different objects directly, you write a "Controller" that you use to encapsulate the later execution of these methods. You can add any kind of code in the controller to do a number of things, like keep track of undo/redo lists, package the commands into batches if you want them executed transactionally, etc.
      In the strategy pattern, you have several chunks of code that do similar things and that you notice naturally already have the same interface that is being invoked in the same way. Or you have a chunk of code that you can identify a simple interface for, and you want to encapsulate it into its own object so that you can write other versions of it with that same interface. So you write a Context object that invokes the interface, and pull out the common bits of code that share that interface and encapsulate them in Strategy objects that all share the same interface. Then you pass the strategy object dynamically to the Context, usually at run-time. This allows you e.g. to select between different strategies at runtime without having to write multiple contexts invoking the same interface.
      Command objects usually all have very different interfaces and you know that you're creating them when you create them. The different interfaces are not abstracted away, instead you use the different interfaces when the different command objects are created, and then you simply call "execute" at a later time to invoke them. In the strategy case, the strategies explicitly have the same interface, you aren't creating strategy objects by using different interfaces, you are usually writing the strategies in code, but then at run time the contexts use the same interface to invoke any one of the strategies.
      In the example Arjan gave, the transactions all had different interfaces: a deposit and a withdrawal took just an account and an amount, but a transfer took a from account, a to account, and an amount. Withdrawal, deposit, and transfer were not three different strategies because they don't have the same interfaces. And the code he was writing wasn't deciding about whether to run a withdrawal, deposit, or transfer at runtime: he had to know which of each he was invoking as he wrote the code, but he invoked them all through a controller that deferred execution of the commands to some later point and managed their execution.

    • @XRay777
      @XRay777 3 года назад

      The TL:DR version:
      Command is about decoupling setup from execution as shown in this video.
      Strategy is about changing the behaviour of your code by swapping components in the code for others with the same interface. E.g. think of swapping the factory containing a tile set to create a level in a video game once you reach the next level.

  • @rohansaraf4493
    @rohansaraf4493 3 года назад

    Really liked the way you explained it. Minor suggestion for the future videos, can you also a little bit explanation how to handle the complexities in case of a distributed systems. Lets see we have different machines and each of them have a bank controller (or we have multiple instances of bank controller) with a common database(store). How will undo/redo work in that case? I am just trying to visualize what kind of complexities can occur?

  • @bombdrive3880
    @bombdrive3880 2 года назад

    15:43 Sorry, I am new to this. But I feel like the code is deeply couple... we call a reference of object's method in "execute, undo, redo" method. So the Deposit class is now couple with Account class. Is it expected?

  • @pthanos
    @pthanos 3 года назад

    Thanks man. I wonder of this pattern can be used to create an approval system. For example, operation x can happen when 2 users have given approval.

  • @mitchedmunds
    @mitchedmunds 3 года назад

    Thanks Arjan, your videos are giving me a lot of ideas to refactor my project using the OOP paradigm. I think I spotted a keyboard shortcut at 27:53 to automatically reposition comma separated values on a single line to each value being on it's own line. Is that one that is readily available in VS Code? Been looking for one that does it without success lately, care to share? Thanks!

    • @rdean150
      @rdean150 2 года назад

      That's likely from his use of the automatic code formatter called "black". VS Code allows you to define a code style engine such as black, autopep8, prettier, and others that will automatically enforce their style rules on your source code while you edit, or apply them across the entire document every time you save the file. Some people love these formatters, some people can't stand them.

  • @DavidFarnan
    @DavidFarnan 3 года назад +3

    Is there a bug with his last example if he uncomments the undo command after the failed batch execute?
    The execute of the batch is adding it to the stack even though it failed and undid itself. When he then just loops the transactions in the batch to undo, he is undoing things that didn't succeed in the first place.
    It seems there are some additional interesting concepts around exception handling for these patterns. He seems to set forth in the batch execute that it should leave the state of the accounts unchanged if an exception occurs, but in the controller you must only be able to undo and redo transactions that succeed thus the failed batch should not have been added to the stack. It seems you could handle this by returning bool from execute indicating if it was successful and thus changed the external state or not, but to me the safest way to handle this is to raise some sort of failed execution exception. Then you could catch that in the controller and not add it to the stack and print the exception failure cause. Now any of my transaction types can raise an exception during execution and the controller can decide is it ok to proceed and not add it to the stack or it is not ok to fail in this execution and the execution bubbles up outside the controller.
    I'm guessing this is all coming in part 2.

    • @SirFloIII
      @SirFloIII 2 года назад

      I suppose the correct thing would be for the batch command to reraise the ValueError after rolling back.

  • @logicerror
    @logicerror 3 года назад +8

    just a random guess: I'm assuming the reason why it isn't autoimporting dataclass is because you are at line 1 of the file. in other words, it would make sense (though perhaps to be considered a bug) that it wouldn't want to "insert" where you are typing. to test this, you could just first add a new line in a new file, then on line 2 see if it will autoimport when you type @dataclass

    • @ArjanCodes
      @ArjanCodes  3 года назад +3

      Interesting... That could be the reason - I'm going to experiment with this, thanks :).

  • @marcelohpinheiro
    @marcelohpinheiro 3 года назад

    Thanks for your video!

  • @JonMarkSearle1
    @JonMarkSearle1 3 года назад +1

    Ajan, I ove your videos. I've been coding since '86, and always find there is more to learn. Recently, I've stated to get a lot more disaplined with the TTD approch; making sure to write a test, failing the test, write code that passes the test, refactor and repeat. I LOVE IT! You don't have to use pytest specifically, but I wonder if you agree with the TDD or BDD approch, whether you might incorporate them into your demos? Also I much prefer the face in the bottom left corner as I'm more interested in the code you write (no offense).

    • @zhonghuahe3556
      @zhonghuahe3556 2 года назад

      also looking forward to seeing more demos accompanying TDD, i found myself difficult using TDD in my daily programming

  • @firefouuu
    @firefouuu 3 года назад

    Great video again ! Just a suggestion: maybe you should run your code inside of the python interactive window instead of the terminal. I feel like it's usually more readable.

  • @sticky319
    @sticky319 3 года назад +1

    Could you consider the transaction classes as the memento pattern? They are storing the state that is used to undo and redo which is one of the main uses of the pattern. As always great video!

    • @RitchieDiamond
      @RitchieDiamond 2 года назад +1

      The memento pattern is related to the usecase presented here, which is to revert an object to a previous state. However, it does this by storing the previous state directly though, instead of modifying the state back and forth like the Command objects are doing here. Mementos are useful to combat side-effects of redoing and undoing which cannot be completely reverted by a Command.

  • @virtualraider
    @virtualraider 3 года назад

    Great video! I'm slowly coming around to protocols, but it still bugs me a lot that one needs to know the protocol specification from memory - no help from the IDE.
    Maybe a solution/workaround is to name the protocol used in the class docstring. That way collaborators and future us can get to the details without guessing 🤔

  • @Antuan2911
    @Antuan2911 2 года назад

    @ArjanCodes
    Does "Protocol" inheritance of "Transaction" class give us the ability
    to pass as argument to "execute" method of "BankController" class
    various classes as "Deposit" class or "Bach" class?
    And also because they have the method "execute" implemented?
    Thank you!

  • @carecavoador
    @carecavoador 2 года назад

    I've been watching all your videos and I really like them. I like your style and the way you present your content, which is amazing. But I'm gonna be honest: Abstract Classes, Protocols and Python Typing are things I never wondered. And now I really want to learn more about it. Do you get to these subjects in your course?

    • @ArjanCodes
      @ArjanCodes  2 года назад

      Thanks, glad you like the videos! Yes, I cover all those topics in detail in the course.

  • @jeancerrien3016
    @jeancerrien3016 3 года назад

    I love how you slip in new pythonisms. I didn't know about "...". I use pass or a comment. Thank you!

  • @alessandroferrari2166
    @alessandroferrari2166 3 года назад

    thank you for teaching all these useful patterns! On our way to become 10X developers 😜

  • @DS-tj2tu
    @DS-tj2tu 3 года назад

    Thank you!

  • @Lodinn
    @Lodinn 3 года назад

    This example was some comedy gold.
    Otherwise, a very helpful and non-obvious pattern!

  • @slinky7355
    @slinky7355 3 года назад

    I know this is all for the sake of theory, but exception handling at a real bank would need to be able to roll back a batch of transactions so that they never occurred in the first place. This is something UniVerse handles at an OS level. You can start a “transaction” which is just a series of file changes, and the OS will track everything and scrub it all away in the event of a rollback.

  • @pinakadhara7650
    @pinakadhara7650 Год назад

    Crazy stuff!

  • @btkb1427
    @btkb1427 3 года назад +1

    Yay another pattern :)

  • @ClemensNyffelerRocks
    @ClemensNyffelerRocks 3 года назад

    Thanks for the awesome videos Arjan. Makes those patterns seem so easy and straightforward.
    I'd love to see your take on the Visitor pattern with python!

    • @ArjanCodes
      @ArjanCodes  3 года назад +1

      Thank you Clemens, good suggestion!

  • @YossiZinger
    @YossiZinger 3 года назад +2

    Arjan: "Google and Microsoft have zero, which is as it should be" (28:51)
    Google: *DEMONITIZE*

    • @ArjanCodes
      @ArjanCodes  3 года назад

      Haha, yes I have to be careful 😊

  • @maddruid840
    @maddruid840 3 года назад

    I'm on the lower side of intermediate, so this may be a dumb question. Would this be a good pattern to use if you think you might move to an MQ system at some point? It seems like it would set you up well to receive and process messages (commands) that were handled by an external queue management system if you wanted to distribute the load. Would this be the right pattern or is there something else more specific to that scenario?

  • @PerisMartin
    @PerisMartin 3 года назад

    3:32 are you talking about Bitcoin here?

  • @addcoding8150
    @addcoding8150 3 года назад

    Arjan Darkmode: 14:00
    Arjan Lightmode: 21:00

    • @ArjanCodes
      @ArjanCodes  3 года назад +1

      Haha, yeah - I'm still figuring out the optimal settings for my camera, but I'm getting there!

  •  3 года назад +1

    ... and part 3 would be the double entry accounting version?
    Jokes aside, great video.

    • @ArjanCodes
      @ArjanCodes  3 года назад

      Haha, that would be nice 😊.

    •  3 года назад

      @@ArjanCodes after bookkeeping for my sports club for 10 years I can only think on double entry. Where transactions are immutable and each contains two or more (account, amount) tuples with a total amount of zero.

  • @LordErnie
    @LordErnie Год назад

    I am a bit new to using behavioral patterns. It to me looks like a couple of things. We want to pass a function, functions aren't values in OO. We pass a command. So, whenever you want to pass a process, or something that represents the executing functionality associated with a process, use a command? It to me really feels like giving a function an explicit type. But now, because classes give us grouping, we associate undo and redo operations with them as well. Is that somewhat accurate? I just really cant thing of an example where i'd use this instead of passing a (sometimes lazy) function. Can you help me out?

  • @southpole76
    @southpole76 6 месяцев назад

    Maybe i'm missing something but why are the commands not inheriting the defined transaction protocol class?

    • @ArjanCodes
      @ArjanCodes  6 месяцев назад +1

      Protocol classes in Python work with duck typing. There’s no need to inherit from a protocol class as Python will infer the type from the methods and properties that are present on the object you pass to a function or method.

  • @shaikirrted2767
    @shaikirrted2767 3 года назад

    In class what it is it called?
    from account : Account
    to account : Account ?

  • @XRay777
    @XRay777 3 года назад

    Does that hint at the end mean we are going to see Flyweight next week? :^)

    • @ArjanCodes
      @ArjanCodes  3 года назад

      Not really. I’m going to show what happens when you change the design to not store the state (account balance) at all but rely on the transaction history instead.

    • @XRay777
      @XRay777 3 года назад

      @@ArjanCodes Looking forward to that. I was thinking that if you want to store a large number of transactions in the history you would turn them into flyweights to save memory 🤔

  • @mishkasensei
    @mishkasensei 3 года назад

    Good video! If you show command design pattern on real simple gui text editor it will be more demonstrably. PS My English maybe is bad, it is not my native:/

  • @JGnLAU8OAWF6
    @JGnLAU8OAWF6 2 года назад

    Undo stack doesn't make sense to me in that context, you generally want to undo specific transactions, not the last one.

  • @BoraCiner
    @BoraCiner Год назад

    music volume is too high

  • @DrSpooglemon
    @DrSpooglemon 3 года назад +1

    I think this is why OOP leads to so much bloat. Instead of just keeping a stack of all the changes and having a undo function all of these classes need to be written. I am creating a chess game in python and I wanted to add undo functionality but I just couldn't justify to myself writing all these classes to handle it. So I have a list named 'history' that I can pop off the previous move. If you wanted redo functionality then all you need is another stack to append the undone actions to.

  • @taylormonacelli
    @taylormonacelli 2 года назад

    You can't really print a bank but if a bank can print money, it's only fair that we should be able to print a bank. Thats funny. Seems fair enough.

  • @foible2085
    @foible2085 3 года назад

    Undo transaction could just create a new transaction with accounts swapped and call execute on that. DRY.

    • @XRay777
      @XRay777 3 года назад

      I am bit on the fence about that, since it clashes with the single responsibility principle. Then the class would have 2 reasons for changing: when its logic changes and when it's dependency changes.

  • @eilmiv
    @eilmiv 2 года назад

    How to get rich:
    1. Withdraw lots of Money from your account as a batch
    2. Undo 🤑

    • @eilmiv
      @eilmiv 2 года назад

      ... assuming you don't have "lots of money" already.

  • @zknarc
    @zknarc 3 года назад +2

    Most of your classes are dataclasses even when they have behaviour (methods). Why is this?

    • @fennecbesixdouze1794
      @fennecbesixdouze1794 3 года назад

      This is a bizarre question. Of course his dataclasses have behavior: they're classes for crying out loud! It's even in the name: dataCLASS.
      If they were just containers for data with no behavior, he could use named tuples or lists or dicts or some other data container (although he may prefer using dataclasses even in that case, idk or care).
      But a dataclass is a *class*, i.e. an outline for objects that have both data and behavior. What characterizes them among other classes is that their objects require no special initialization apart from providing values to the fields.
      The assumption that your object's behavior and identity depends only on its fields, because there is no special initialization, gives you a lot of nice guarantees and enables you to build out all kinds of nice functionality in such a class for free with a bit of boilerplate. With Python, a lot of the boilerplate for that nice functionality is done for you with the dataclass decorator.

    • @ArjanCodes
      @ArjanCodes  3 года назад +3

      The main reason is that I find dataclasses help a lot in reducing boilerplate code, in particular writing class initializers and methods to convert an object into a string.

  • @davidmurphy563
    @davidmurphy563 3 года назад

    Nah, I tried that approach with my marriage. Didn't work.

  • @AloisMahdal
    @AloisMahdal Год назад

    Withdraw all money from Google and Microsoft and watch the world BURN from Bahamas! 😀