一、命令模式的定義
命令(Command)模式的定義如下:將一個請求封裝為一個對象,使發出請求的責任和執行請求的責任分割開。這樣兩者之間通過命令對象進行溝通,這樣方便將命令對象進行儲存、傳遞、調用、增加與管理。
二、命令模式優缺點
命令模式的主要優點如下:
- 降低系統的耦合度。命令模式能將調用操作的對象與實現該操作的對象解耦。
- 增加或刪除命令非常方便。采用命令模式增加與刪除命令不會影響其他類,它滿足“開閉原則”,對擴展比較靈活。
- 可以實現宏命令。命令模式可以與組合模式結合,將多個命令裝配成一個組合命令,即宏命令。
- 方便實現 Undo 和 Redo 操作。命令模式可以與后面介紹的備忘錄模式結合,實現命令的撤銷與恢復。
其缺點是:
- 可能產生大量具體命令類。因為計對每一個具體操作都需要設計一個具體命令類,這將增加系統的復雜性。
三、命令模式的實現
可以將系統中的相關操作抽象成命令,使調用者與實現者相關分離,命令模式包含以下主要角色:
- 抽象命令類(Command)角色:聲明執行命令的接口,擁有執行命令的抽象方法 execute()。
- 具體命令角色(Concrete Command)角色:是抽象命令類的具體實現類,它擁有接收者對象,並通過調用接收者的功能來完成命令要執行的操作。
- 實現者/接收者(Receiver)角色:執行命令功能的相關操作,是具體命令對象業務的真正實現者。
- 調用者/請求者(Invoker)角色:是請求的發送者,它通常擁有很多的命令對象,並通過訪問命令對象來執行相關請求,它不直接訪問接收者。
其結構圖如圖所示:
代碼實現如下:
public class CommandPattern { public static void main(String[] args) { Command cmd=new ConcreteCommand(); Invoker ir=new Invoker(cmd); System.out.println("客戶訪問調用者的call()方法..."); ir.call(); } } //調用者 class Invoker { private Command command; public Invoker(Command command) { this.command=command; } public void setCommand(Command command) { this.command=command; } public void call() { System.out.println("調用者執行命令command..."); command.execute(); } } //抽象命令 interface Command { public abstract void execute(); } //具體命令 class ConcreteCommand implements Command { private Receiver receiver; ConcreteCommand() { receiver=new Receiver(); } public void execute() { receiver.action(); } } //接收者 class Receiver { public void action() { System.out.println("接收者的action()方法被調用..."); } }
執行結果如下:
客戶訪問調用者的call()方法...
調用者執行命令command...
接收者的action()方法被調用...
四、命令模式的應用場景
命令模式通常適用於以下場景。
- 當系統需要將請求調用者與請求接收者解耦時,命令模式使得調用者和接收者不直接交互。
- 當系統需要隨機請求命令或經常增加或刪除命令時,命令模式比較方便實現這些功能。
- 當系統需要執行一組操作時,命令模式可以定義宏命令來實現該功能。
- 當系統需要支持命令的撤銷(Undo)操作和恢復(Redo)操作時,可以將命令對象存儲起來,采用備忘錄模式來實現。
五、命令模式的擴展
在軟件開發中,有時將命令模式與前面學的組合模式聯合使用,這就構成了宏命令模式,也叫組合命令模式。宏命令包含了一組命令,它充當了具體命令與調用者的雙重角色,執行它時將遞歸調用它所包含的所有命令,其具體結構圖如圖所示:
代碼如下:
public class CompositeCommandPattern { public static void main(String[] args) { AbstractCommand cmd1=new ConcreteCommand1(); AbstractCommand cmd2=new ConcreteCommand2(); CompositeInvoker ir=new CompositeInvoker(); ir.add(cmd1); ir.add(cmd2); System.out.println("客戶訪問調用者的execute()方法..."); ir.execute(); } } //抽象命令 interface AbstractCommand { public abstract void execute(); } //樹葉構件: 具體命令1 class ConcreteCommand1 implements AbstractCommand { private CompositeReceiver receiver; ConcreteCommand1() { receiver=new CompositeReceiver(); } public void execute() { receiver.action1(); } } //樹葉構件: 具體命令2 class ConcreteCommand2 implements AbstractCommand { private CompositeReceiver receiver; ConcreteCommand2() { receiver=new CompositeReceiver(); } public void execute() { receiver.action2(); } } //樹枝構件: 調用者 class CompositeInvoker implements AbstractCommand { private ArrayList<AbstractCommand> children = new ArrayList<AbstractCommand>(); public void add(AbstractCommand c) { children.add(c); } public void remove(AbstractCommand c) { children.remove(c); } public AbstractCommand getChild(int i) { return children.get(i); } public void execute() { for(Object obj:children) { ((AbstractCommand)obj).execute(); } } } //接收者 class CompositeReceiver { public void action1() { System.out.println("接收者的action1()方法被調用..."); } public void action2() { System.out.println("接收者的action2()方法被調用..."); } }
測試結果如下:
客戶訪問調用者的execute()方法...
接收者的action1()方法被調用...
接收者的action2()方法被調用...