◆ 前言:命令模式內容比較多,這里做了拆分
命令模式基礎篇 :http://www.cnblogs.com/JsonShare/p/7202133.html
命令模式擴展篇 - 宏命令:http://www.cnblogs.com/JsonShare/p/7206395.html
命令模式擴展篇 - 撤銷命令:http://www.cnblogs.com/JsonShare/p/7206513.html
命令模式擴展篇 - 命令隊列:http://www.cnblogs.com/JsonShare/p/7206607.html
命令模式擴展篇 - 請求日志:http://www.cnblogs.com/JsonShare/p/7206665.html
1、簡介
定義:將一個請求封裝為一個對象,從而使你可以用不同的請求對客戶進行參數化,對請求排隊和記錄請求日志,以及支持可撤銷的操作;
主要解決:在軟件系統中,行為請求者與行為實現者通常是一種緊耦合的關系,但某些場合,比如需要對行為進行記錄、撤銷或重做、事務等處理時,這種緊耦合的設計就不太合適;
英文:Command
類型:行為型模式
2、類圖及組成
(引)類圖:
組成:
● Command(抽象命令類):抽象命令類一般是一個抽象類或接口,在其中聲明了用於執行請求的execute()等方法,通過這些方法可以調用請求接收者的相關操作。
● ConcreteCommand(具體命令類):具體命令類是抽象命令類的子類,實現了在抽象命令類中聲明的方法,它對應具體的接收者對象,將接收者對象的動作綁定其中。在實現execute()方法時,將調用接收者對象的相關操作(Action)。
● Invoker(調用者):調用者即請求發送者,它通過命令對象來執行請求。一個調用者並不需要在設計時確定其接收者,因此它只與抽象命令類之間存在關聯關系。在程序運行時可以將一個具體命令對象注入其中,再調用具體命令對象的execute()方法,從而實現間接調用請求接收者的相關操作。
● Receiver(接收者):接收者請求與執行相關的操作,它具體實現對請求的業務處理。
● Client(客戶端角色類):最終的客戶端調用類。
代碼結構:
抽象命令角色類 public interface Command { /** * 執行方法 */ void execute(); } 具體命令角色類 public class ConcreteCommand implements Command { //持有相應的接收者對象 private Receiver receiver = null; /** * 構造方法 */ public ConcreteCommand(Receiver receiver){ this.receiver = receiver; } @Override public void execute() { //通常會轉調接收者對象的相應方法,讓接收者來真正執行功能 receiver.action(); } } 接收者角色類 public class Receiver { /** * 真正執行命令相應的操作 */ public void action(){ System.out.println("執行操作"); } } 請求者角色類 public class Invoker { /** * 持有命令對象 */ private Command command = null; /** * 構造方法 */ public Invoker(Command command){ this.command = command; } /** * 行動方法 */ public void action(){ command.execute(); } } 客戶端角色類 public class Client { public static void main(String[] args) { //創建接收者 Receiver receiver = new Receiver(); //創建命令對象,設定它的接收者 Command command = new ConcreteCommand(receiver); //創建請求者,把命令對象設置進去 Invoker invoker = new Invoker(command); //執行方法 invoker.action(); } }
3、實例引入
下面以電視機的開、關命令為例,進行講解:
package com.designpattern.Command; /** * 抽象命令角色類 * @author Json */ public interface Command { /** * 執行方法 */ void execute(); }
package com.designpattern.Command; /** * 接收者角色類 -- 電視 * @author Json */ public class TV { /** * 打開方法 */ public void on(){ System.out.println("電視打開"); } /** * 關閉方法 */ public void off(){ System.out.println("電視關閉"); } }
package com.designpattern.Command; /** * 具體命令角色類 -- 電視打開命令 * @author Json */ public class TVOnCommand implements Command { private TV tv; public TVOnCommand(TV tv){ this.tv = tv; } @Override public void execute() { tv.on(); } }
package com.designpattern.Command; /** * 具體命令角色類 -- 電視關閉命令 * @author Json */ public class TVOffCommand implements Command { private TV tv; public TVOffCommand(TV tv){ this.tv = tv; } @Override public void execute() { tv.off(); } }
package com.designpattern.Command; /** * 請求者角色類 -- 遙控器Invoker * @author Json */ public class RemoteControl { private Command onCommand,offCommand; public RemoteControl(Command _on,Command _off){ this.onCommand = _on; this.offCommand = _off; } public void turnOn(){ onCommand.execute(); } public void turnOff(){ offCommand.execute(); } }
測試:
package com.designpattern.Command; /** * 客戶端角色類 * @author Json */ public class Client { public static void main(String[] args) { //創建接收者 TV receiver = new TV(); //創建命令對象,設定它的接收者 Command on_command = new TVOnCommand(receiver); //創建命令對象,設定它的接收者 Command off_command = new TVOffCommand(receiver); //命令控制對象Invoker,把命令對象通過構造方法設置進去 或者 增加set方法set進去是一樣的(setCommand方法未寫) RemoteControl invoker = new RemoteControl(on_command,off_command); //執行方法 -- 打開電視 invoker.turnOn(); //執行方法 -- 關閉電視 invoker.turnOff(); } }
結果:
電視打開
電視關閉
關於例子的補充說明:
雖然代碼看似挺多,但其實命令模式的結構還是比較清晰的,總的來說命令模式的使用流程就是首先創建一個抽象命令,然后創建多個具體命令實現抽象命令接口,然后創建一個命令接受者角色,它包含各種的行為的具體實現,然后再有一個命令調用者角色,提供給客戶端,用於接收客戶端的參數;
4、命令模式的擴展
由於篇幅太長,這里單獨拆分出去了;
傳送門:
命令模式擴展篇 - 宏命令:http://www.cnblogs.com/JsonShare/p/7206395.html
命令模式擴展篇 - 撤銷命令:http://www.cnblogs.com/JsonShare/p/7206513.html
命令模式擴展篇 - 命令隊列:http://www.cnblogs.com/JsonShare/p/7206607.html
命令模式擴展篇 - 請求日志:http://www.cnblogs.com/JsonShare/p/7206665.html
5、優缺點
優點:
1、命令模式將行為調用者和各種行為分隔開,降低程序的耦合,便於程序擴展;
2、命令模式將行為的具體實現封裝起來,客戶端無需關心行為的具體實現;
3、命令模式可為多種行為提供統一的調用入口,便於程序對行為的管理和控制;
缺點:
使用命令模式的話,不用管命令多簡單,都需要寫一個命令類來封裝,使用命令模式可能會導致系統有過多的具體命令類;
(上面的例子就可以看出,每個命令都要有一個具體的命令類);
6、應用場景
1、希望將行為請求者和行為實現者解耦,不直接打交道;
2、希望分離掉行為請求者一部分的責任,行為請求者只需要將命令發給調用者,不再主動的去讓行為實現者產生行為,符合單一職責原則;
3、希望可以控制執行的命令列表,方便記錄,撤銷/重做以及事務等功能;
4、期待可以將請求排隊,有序執行;
5、希望可以將請求組合使用,即支持宏命令;
7、總結
命令模式最大的好處就是實現了行為請求者與行為實現者的解耦;
在實際場景中的使用:
Struts2中action中的調用過程中存在命令模式;
數據庫中的事務機制的底層實現;
命令的撤銷和恢復:增加相應的撤銷和恢復命令的方法(比如數據庫中的事務回滾);
例如:Java的Runnable就是命令模式的變形應用:
public class Test { public static void main(String[] args) { //范例 Runnable runnable = () -> System.out.println("具體命令"); // Command cmd = ConcreteCommand Thread thread1 = new Thread(runnable); // 將 cmd 交給 Thread (Invoker) thread1.start(); // Invoker 調用 cmd 的執行方法 } }
PS:源碼地址 https://github.com/JsonShare/DesignPattern/tree/master
PS:原文地址 http://www.cnblogs.com/JsonShare/p/7202133.html