no_mkd
1. What is the Command Pattern?
The Command Pattern encapsulates method call details to decouple the requester from the executor. The specific flow is as follows:
1. From the requester's (client's) perspective
Requester (client) makes a request -> Invoker (system) constructs a command object to encapsulate the request -> Invoker calls the command object's specified method (the request is executed). Obviously, the requester doesn't know who the executor is, let alone the specific execution details. Of course, the requester doesn't care about these either; it only needs to know that the request has been executed.
2. From the executor's (low-level component's) perspective
Executor (low-level component) is called -> Executor calls its internal method (the request is executed). Similarly, the executor doesn't know who the requester is, and doesn't even know about the invoker. That's fine; the executor only needs to do its job conscientiously without needing to know about the leadership's situation.
3. From the invoker's (system's) perspective
Receive request -> Create command object to encapsulate request -> Call the command object's action at the appropriate time to execute the request (the request is executed). The invoker doesn't know who the executor is, nor does it know the requester. It's only responsible for constructing commands and controlling their execution, which is sufficient.
From the above, we can see the low-coupling relationship between objects:
The requester (client) and executor (low-level component) are thoroughly decoupled. As the middleman, the invoker also doesn't understand the specific details of the requester and executor; they are well protected. This is exactly what we want.
2. An Example
Any slightly complex subsystem in the real world should have a set of commands, such as a restaurant's operating mechanism:
Customer A comes to the restaurant and orders a bowl of noodles (makes a request) -> Counter waiter records it (creates a command) -> Waiter throws the ticket to the kitchen -> Chef C quickly makes a bowl of noodles (request is executed). The customer doesn't know who will make the noodles, and the counter waiter doesn't know either. The chef doesn't know who ordered the noodles; they only know they can rest after finishing the noodles. Isn't this very similar to the Command Pattern?
Let's implement the above mechanism with code. First, we need a command interface. After all, commands are the core of the Command Pattern. Without commands, everything is wishful thinking:
package CommandPattern;/**
- @author ayqy
- Define Command interface */ public interface Command { public abstract void execute();//Only need to define a unified execution method }
With the command, we also need an executor. Otherwise, having only generals without soldiers, the restaurant's executor is of course the chef:
package CommandPattern;/**
-
@author ayqy
-
Define Chef base class */ public abstract class Chef { //Define common properties for chefs here
/**
- Define cooking method */ public abstract void cook(); //Define other useful methods here }
We also need to implement specific chefs, as different chefs specialize in different things:
Chef who makes noodles:
package CommandPattern;/**
-
@author ayqy
-
Define professional noodle chef */ public class NoodlesChef extends Chef{
@Override public void cook() { System.out.println("Made a delicious bowl of hand-pulled noodles"); } }
Chef who makes flatbread:
package CommandPattern;/**
-
@author ayqy
-
Define professional flatbread chef */ public class PieChef extends Chef{
@Override public void cook() { System.out.println("Made a fragrant flatbread"); } }
With soldiers and generals, we also need a complete set of commands:
package CommandPattern;/**
-
@author ayqy
-
Implement concrete NoodlesCommand */ public class NoodlesCommand implements Command{ private NoodlesChef chef;//Professional noodle chef
public NoodlesCommand(){ chef = new NoodlesChef(); }
@Override public void execute() { chef.cook(); //Call other needed methods } }
package CommandPattern;
/**
* @author ayqy
* Implement concrete PieCommand
*/
public class PieCommand implements Command{
private PieChef chef;//Professional flatbread chef
public PieCommand(){
chef = new PieChef();
}
@Override
public void execute() {
chef.cook();
//Call other needed methods
}
}
The preparation work is done; the restaurant can open for business.
3. Effect Example
Need a Test class:
package CommandPattern;/**
-
@author ayqy
-
Implement test class */ public class Test {
public static void main(String[] args) { System.out.println("Command Pattern restaurant opens for business..."); System.out.println("First customer Mr. X"); System.out.println("Mr. X: Hello, I need a bowl of noodles, I'm starving"); NoodlesCommand nCmd = new NoodlesCommand(); System.out.println("Counter waiter: Okay, I've noted it down, it'll be ready soon"); System.out.println("Counter waiter: Kitchen~, order received"); nCmd.execute(); System.out.println("Mr. X: So fast!");
System.out.println(); System.out.println("Second customer Mr. XX"); System.out.println("Mr. XX: Hello, I need a flatbread, I'll pick it up in 20 minutes"); PieCommand pCmd = new PieCommand(); System.out.println("Counter waiter: Okay, I've noted it down"); System.out.println("15 minutes later"); System.out.println("Counter waiter: Kitchen~, order received"); pCmd.execute(); System.out.println("Mr. XX: So punctual!");} }
Result example:

From the example, we can see:
- The invoker (counter waiter) can control the specific execution timing, but has no idea about the details of the specific executor (chef)
- The requester (customer) has no idea about the restaurant's operating mechanism, doesn't know whether the ordered meal is made by the chef, the waiter, or bought from next door...
- The executor (chef) has no idea about the requester's situation; it only does its job, and knows nothing else about the rest
4. Extensions of the Command Pattern
1. Macro Command (multiple commands executed in sequence)
We can implement this by defining a "command of commands" (the execute method of this special command internally calls the execute methods of other commands in sequence...)
2. Undo
Suppose many customers come and order many meals. After a while, some customers can't wait and need to cancel. How do we implement this? Maintain a command list to record already created commands. To cancel, we need to find the corresponding command and execute the undo operation. Of course, the premise is that the command object supports undo, so we need to make some modifications:
package CommandPattern;/**
-
@author ayqy
-
Define Command interface */ public interface Command { public abstract void execute();//Only need to define a unified execution method
public abstract void undo();//Define a unified undo method }
The undo operation of each command may be different, so it's defined as an abstract method to be implemented by subclasses.
*How to support multi-step sequential undo? The restaurant example may not need such functionality. Let's think of another scenario: a text editor. The user issues a series of commands and completes a series of operations. Later, they find that these operations are not needed, and the user will undo the changes (Ctrl + Z). At this point, we need to execute the opposite operation to restore the content. How to implement this?
We still need to implement the undo behavior of each command first (execute operations in the opposite order of execute). In addition, we also need a stack to record operations that have been executed to support undoing to the initial state.
3. Queued Requests
We can establish a worker thread responsible for all operations. Imagine there's a channel where the input is different commands, and the output is the execution results of the commands. Perhaps one moment the worker thread is making flatbread, and the next moment it's already out buying groceries...
*What's the benefit of doing this? Operations can be restricted to specific threads for control.
4. Logged Requests
Often used in the implementation of database management systems. We need to record a series of operations (such as writing to a hard drive). When a system failure occurs, read them out to recover data. How to implement this? Use object serialization to save objects (record logs), and deserialize when needed (recover transactions).
5. Summary
The Command Pattern can effectively decouple requesters from executors, and can also provide some additional benefits (such as supporting undo operations, queued requests, recording logs, etc.)
No comments yet. Be the first to share your thoughts.