"Command Design Pattern": Elevate Code Control and Flexibility, Simplify User Actions Undo/Redo, and Beyond!
In this article we continue with the series of behavioral design patterns. This is the time for the "Command Pattern" (1.).
The Command Design Pattern is a behavioral design pattern that addresses the problem of decoupling the sender of a request from its receiver. It encapsulates a request as an object, allowing clients to parameterize with different requests, queue or log requests, and support undoable operations. This pattern promotes flexibility, maintainability, and extensibility in software systems, making it easier to manage complex interactions between objects.
The Command Pattern is particularly useful (I would say essential) in a number of situations where it is necessary to manage user actions in a flexible way and simplifying the management of complex interactions.
So let's start immediately with an example that clarifies the situation.
As an example let's consider "user interface toolkits". The UI Toolkits include objects like buttons and menus that carry out a request in response to user input. But the toolkit can't implement the request explicitly in the button or menu, because only applications that use the toolkit know what should be done on which object. As toolkit designers we have no way of knowing the receiver of the request or the operations that will carry it out.
The Command model suggests that GUI objects shouldn't submit these requests directly. Instead, we should extract all the request details, such as the called object , the name of the method and the list of arguments in a separate command class with a single method that triggers this request.
Command objects act as links between various GUIs and business logic objects. From now on, the GUI object does not need to know which business logic object will receive the request and how it will be processed. The GUI object only activates the command, which handles all the details.
The key to this pattern is an abstract Command class, which declares an interface for executing operations. In the simplest form this interface includes an abstract "execute" operation.
Concrete Command subclasses specify a receiver-action pair by storing the receiver as an instance variable and by implementing execute to invoke the request.
The receiver has the knowledge required to carry out the request. Let's see the following example.
The Menus can be implemented easily with Command objects. Each choice in a Menu is an instance of a MenuItem class. An Application class creates the menu and its menu items. The Application class also keeps track of which Document objects the user has opened.
The application configures each MenuItem with an instance of a concrete Command subclass. When the user selects a MenuItem, the MenuItem calls the "execute" method on the related command so performing the operation.
The class diagram shown below clarifies the structure of the solution.
For example, PasteCommand supports pasting text from the clipboard into a Document. The receiver of the PasteCommand is the Document object which is supplied to the instance. The execute operation invokes Paste on the receiving document. Let's see the snippet diagram below.
OpenCommand's execute operation is different: it prompts the user for a document name, creates a corresponding Document object, adds the document to the receiving application, and opens the document. Let's see the snippet diagram below.
In each of these examples, notice how the Command pattern decouples the object that invokes the operation from the one having the knowledge to perform it.
This gives us a lot of flexibility in designing our user interface. An application can provide both a menu and a push button interface to a feature just by making the menu and the push button share an instance of the same concrete Command subclass. We can replace commands dynamically, which would be useful for implementing context-sensitive menus. We can also support command scripting by composing commands into larger ones.
All of this is possible because the object that issues a request only needs to know how to issue it; it doesn't need to know how the request will be carried out.
Now let's see if there are real-world applications of the command pattern. In a diner setting, the "order" can be an excellent real-world example of the Command Design Pattern. Let's explore how the Command pattern can be applied to manage the process of generating a customer's order, managing dish prep, and managing the bill in a diner.
These are the components of the Command Pattern for diner management:
Below we see an example of a sequence diagram relating to the implementation of the command pattern for managing the customer's order in a diner:
The waiter or waitress takes an order or command from a customer and encapsulates that order by writing it on the check. The order is then queued for the kitchen. Note that the chefs and the cashier are the receivers, the chef will prepare the dishes and the cashier will print the check.
Now let's see the implementation of the code in java.
In the example we have considered the presence of two chef, "Stefano e "Luisa".
As you can see we have also considered the preferences in order management such as "rare" for the steak and "without vinegar" for the salad.
This information will be used by the chef when preparing the dishes of the order.
I also considered having two chef in the kitchen. The waiter taking the order is the one who decides which chef the order is given to.
As we can see from the console picture the program seems to work fine.
Below we can see the "MenuItem" data class and the "Command" interface on which the pattern is based and then we also see two concrete commands: "AddItemCommand" e "RemoveItemCommand".
Below you can see the Invoker class of the "Waiter" and the two Receiver classes "Chasier" and "Chef":
At this point, after we saw two examples of command pattern implementations, the "toolkit menu" and the "dinner order", the time has come to examine the general structure of the command pattern in detail.
Let's start with the static structure : the class diagram.
The class diagram for the command pattern includes the following components:
And now let's see the dynamic behavior of the pattern.
The dynamic object collaboration is shown in the sequence diagram below where we could see the interactions between the pattern components.
Well, now that we've seen the structure of the pattern in detail, let's look at a couple of scenarios where the pattern command can be very useful.
Imagine we have a universal remote control that can operate various electronic devices like TVs, DVD players, and sound systems. The remote control has multiple buttons, each representing a different function (e.g., turn on, turn off, increase volume, decrease volume, change the channel, etc.). The Command Pattern helps in implementing this functionality effectively.
The components in action are always the same:
I wrote a conceptual example with a TV object and a remote control object, I wrote it in C++ language, and below we see the source code.
In this example, we've created a simple TV receiver and two command objects: TurnOnCommand and TurnOffCommand. The RemoteControl acts as the invoker. When you press a button on the remote control, it sets the appropriate command and then executes it, which triggers the corresponding action on the TV.
And now let's see another scenario of applying the pattern command
In concurrent programming, the Command Design Pattern can be useful for managing multi-threading scenarios and task queues. Commands can represent tasks or operations that need to be executed concurrently. The invoker maintains a queue of commands, and multiple threads can process these commands concurrently, allowing for efficient use of system resources.
The components in action are always the same:
I wrote a conceptual example where we have a task queue that can execute commands (tasks) concurrently using a thread pool. I wrote it in Kotlin language.
In this example, there is a TaskQueue acting as the invoker that manages a thread pool with two worker threads. There are also two concrete commands, Task1 and Task2, each representing a specific task to be executed concurrently. When we add a task to the TaskQueue, it is executed by one of the worker threads from the thread pool. The WorkerThread class represents the receiver, which executes the actual task.
Before finishing this analysis on the command pattern, let's examine the relationship between the Command Design Pattern and each of the SOLID principles (.2,.3.4.5.6):
That's it for the "Command Pattern".
I remind you my newsletter?"Sw Design & Clean Architecture"? :?https://lnkd.in/eUzYBuEX?where you can find my previous articles and where you can register, if you have not already done, so you will be notified when I publish new articles.
?
Thanks for reading my article, and I hope you have found the topic useful,
Feel free to leave any feedback.
Your feedback is very appreciated.
Thanks again.
Stefano
?
References:
1.?Gamma, Helm, Johnson, Vlissides, “Design Patterns”, Addison Wesley (2° Edition October 2002). pp 199-207.
2.?S. Santilli: "https://www.dhirubhai.net/pulse/single-responsibility-principle-stefano-santilli/".?
3.?S. Santilli: "https://www.dhirubhai.net/pulse/open-closed-principle-stefano-santilli/".?
4.?S. Santilli: "https://www.dhirubhai.net/pulse/squarerectangle-problem-stefano-santilli/".?
5.?S. Santilli: "https://www.dhirubhai.net/pulse/interface-segregation-principle-isp-stefano-santilli/".
6.?S. Santilli: "https://www.dhirubhai.net/pulse/dependency-inversion-principle-stefano-santilli/".?
IT Support Specialist, Designer, Developer
1 年Dear Stefano Santilli ????, I would like to express my deep gratitude and heartfelt thanks for the outstanding article , I found it to be truly inspiring and immensely valuable. I greatly appreciated your writing style and the organization of the information. You provided clear examples and illustrative code snippets that made it easy to comprehend complex concepts. Additionally, your ability to highlight practical use cases and real-world scenarios for the design pattern helped solidify my understanding of the topic.