命令模式 Command Pattern
981 字
5 分鐘
命令模式 Command Pattern
目的
將訊息(請求)封裝成物件,可以參數化具有不同請求、佇列或日誌請求的Client,並且支援復原動作。
適用
- 想用「欲執行之動作」來參數化物件。(登記成Callback Function)。在不同時間、佇列、執行命令時。支援復原動作。支援日誌功能,系統Crash時能再重新執行。以基礎操作組出高階的系統(常見於支援交易功能的資訊系統中)。
結構及成員
Collaborations:
Client建立ConcreteCommand物件並設定Receiver。
Invoker物件會將ConcreteCommand存起來。
Invoker呼叫所存Command之Execute(),若Command是可復原的,ConcreteCommand就會在執行Execute()前先儲存狀態。
ConcreteCommand物件呼叫所載Receiver之操作,以真正執行命令。

- Command:制訂執行命令之介面。ConcreteCommand: like the CopyCommand, PasteCommand將Receiver物件和對應的動作繫結起來。實作Execute():呼叫Receiver的對應操作。Client: like the Application建立ConcreteCommand物件並設定它的Receiver。Client基本上會持有Invoker並使用setCommand設定ConcreteCommand。Invoker: like the MenuItem要求Command執行命令。Receiver: like the Document, Application, etc.知道如何根收到的訊息執行命令,任何類別都可以是Receiver。
影響結果
好處
- Command將「引發命令的物件」與「知道如何執行的物件」隔離開來。Command是一級物件(First-Class Object),可和一般物件一樣使用及擴充。容易新增新的Command類型,不必更改既有類別。
壞處
- 會導致大量瑣碎的命令子類別。
實作
Command該有多聰明?
一個極端是只定義訊息接收者和執行動作之間的繫結關係,將任務全部委託給接收者,另一個極端是全都自己來,不轉傳給接收者。
- 若想把Command與既有類別彼此獨立,或沒有合用的接收者存在,或Command自己知道接收者是誰(如:再產生一個視窗),那可以使用後者。在這兩個極端中間,Command必須具有足夠的知識動態尋得訊息接收者。
支援復原與重做
只要有辦法逆轉執行動作,Command就有復原及重做的能力。故ConcreteCommand可能得存放以下這些狀態:
- Receiver物件,負責實際執行所要求的命令。餵給Receiver對應操作的參數。在執行操作後,所有可能有變動的Receiver原始內容。Receiver必須提供某些操作以供復原。
若想支援一層復原,則必須儲存最後執行的Command;若要多層,就要一個Command History List。
置入歷程列表前,若Command執行後狀態會變(如:DeleteCommand),就先複製一份,確保每次啟用的Command都是乾淨的。(i.e. Prototype Pattern)
避免復原過程中不斷累積錯誤
在Command裡多存一點資訊,以確保物件能真的回復到最初狀態。可用Memento Pattern來存。
Example: SimpleRemoteControl
public class Light {
public Light() {}
public void on() { System.out.println("Light is on"); }
public void off() { System.out.println("Light is off"); }
}public class GarageDoor {
public GarageDoor() {} public void up() { System.out.println("Garage Door is Open"); }
public void down() { System.out.println("Garage Door is Closed"); }
public void stop() { System.out.println("Garage Door is Stopped"); }
public void lightOn() { System.out.println("Garage light is on"); }
public void lightOff() { System.out.println("Garage light is off"); }public void Off() { System.out.println("Garage Door is off"); }}public interface Command { public void execute();}public class GarageDoorOpenCommand implements Command { //Have its suitable receiver GarageDoor garageDoor; //Specify the Constructor public GarageDoorOpenCommand (GarageDoor garageDoor) { this.garageDoor = garageDoor; }
//Execute the garageDoor.up() function @Override public void execute() { // TODO Auto-generated method stub garageDoor.up(); }
}//Then describe the LightOnCommand implements Commandpublic class LightOnCommand implements Command {
Light light;
public LightOnCommand (Light light) { this.light = light; }
@Override public void execute() { // TODO Auto-generated method stub light.on(); }
}public class SimpleRemoteControl {
Command slot;
public SimpleRemoteControl() {}
//Describe the important function of the invoker public void setCommand(Command command) { this.slot = command; }
public void buttonWasPressed() { slot.execute(); }}public class SimpleRemoteControlClient{
public static void main(String[] args) { Light light = new Light(); GarageDoor garageDoor = new GarageDoor(); SimpleRemoteControl remoteControl = new SimpleRemoteControl();
Command lightOnCommand = new LightOnCommand(light); remoteControl.setCommand(lightOnCommand); remoteControl.buttonWasPressed();
Command garageDoorOpenCommand = new GarageDoorOpenCommand(garageDoor ); remoteControl.setCommand(garageDoorOpenCommand ); remoteControl.buttonWasPressed(); }}Result:
Light is onGarage Door is Open文章分享
如果這篇文章對你有幫助,歡迎分享給更多人!
命令模式 Command Pattern
https://linziyou.info/posts/2020-10-06-命令模式-command-pattern/ 最後更新於 2020-10-06,距今已過 1973 天
部分內容可能已過時