命令模式也是一種比較常見的行爲型模式,能夠想象咱們的手機智能遙控器,經過按動按鈕的形式開啓各類傢俱,說白了,就是將一系列的請求命令封裝起來,不直接調用真正執行者的方法,這樣比較好擴展。須要注意的是命令模式和策略模式類似,因此有時候可能容易弄混,這篇文章將會詳細介紹命令模式java
解釋:把「請求」封裝爲對應的對象,使用不一樣的請求參數化對象,命令模式支持撤銷撤銷的操做設計模式
命令模式是一種行爲型模式,實現了接口調用對象和返回對象,用命令對象做爲橋樑實現調用者和具體實現者之間的解耦和交互。數組
下面根據命令模式的定義,以及上面對於命令模式的理解,構建具體的結構圖服務器
+ Client 客戶端:客戶端須要建立具體的命令類,而且經過發送請求給執行者調用具體的對象,發送方和接收方不存在關聯,統一由命令對象進行鏈接。ide
+ Invoker 執行者:請求的發送者,負責將請求分發給具體的命令實現類,由實現類調用實際的執行者進行執行操做單元測試
+ Command 接口:命令接口,定義命令的規範測試
+ ConcreteCommand 命令接口實現類:實現命令的同時組合具體對象。this
+ ConcreteObject 具體實現類:定義截圖的實現生產對象。spa
+ Receive 執行者:請求的真正執行者,能夠是任意對象,一般以 組合形式出如今執行者的內部設計
這裏參考《Head firtst設計模式》的案例,模擬具體的交互流程
咱們到餐廳點餐,通常會經歷以下的流程
根據上面的步驟利用僞代碼的表現以下:
createCommandObject()
構建命令對象setCommand()
傳遞命令execute()
命令執行action1()
,action2()
執行者實際執行咱們根據上面的交互過程介紹,構建具體的交互流程圖,咱們能夠看到裏面有角色:客人
、服務員
、訂單櫃檯
、廚師
,他們自己並無關聯,而是經過餐廳的形式彼此產生了具體的關聯,同時咱們對比上面的結構圖,看下對象村餐廳對應的結構圖:
下面根據結構圖說一下各類角色的職責:
客人:至關於client客戶端,負責指揮服務員進行下單的操做。
服務員:充當請求的發送者,接受客戶端的請求,調用下訂單的接口到具體的訂單櫃檯,可是不須要關心具體的細節,只具有下訂單這一個操做
訂單櫃檯:經過服務員傳遞的訂單,安排廚師執行具體的任務
廚師:根據訂單櫃檯的訂單作菜,將結果返回給服務員(或客人)
咱們從上面的角色圖再來看具體的命令模式定義,能夠看到基本都是一一對應的狀況。
命令模式和策略模式的結構圖有些許的相似,下面咱們來對比看一下這兩張圖的異同:
策略模式結構圖:
命令模式結構圖:
相同點:
不一樣點:
此次的案例仍是模擬《Head First》設計模式的當中對於遙控器遙控電器的一個案例,咱們定義以下的內容:
遙控器:命令的發送方,負責根據不一樣的操做按鈕調用不一樣的設備工做,生成具體的命令對象調用接口執行具體的命令
命令接口:負責定義命令的實現規範,充當遙控器裏面的每個按鈕,對應都有具體的實現
命令實現類:負責實現命令的接口,同時調用具體的實現對象執行命令
實現對象:命令的真正執行者,通常夬在命令實現類的內部,好比電視,燈泡等
在不使用設計模式的狀況下,咱們一般經過對象組合的形式組合不一樣的實體對象執行命令,下面經過一些簡單的代碼說明一下設計的弊端:
// 燈泡 public class Light { public void on(){ System.out.println("打開燈光"); } public void off(){ System.out.println("關閉燈光"); } } // 電視機 public class Television { public void on(){ System.out.println("打開電視"); } public void off(){ System.out.println("關閉電視"); } } // 遙控器 public class RemoteControl { private Light light; private Television television; public RemoteControl(Light light, Television television) { this.light = light; this.television = television; } public void button1(){ light.on(); } public void button2(){ television.on(); } } // 單元測試 public class Main { public static void main(String[] args) { Television television = new Television(); Light light = new Light(); RemoteControl remoteControl = new RemoteControl(light, television); remoteControl.button1(); remoteControl.button2(); } }/*運行結果: 打開燈光 打開電視 */
從上面的簡單代碼能夠看到,若是咱們繼續增長電器,同時增長方法,不只會致使遙控器要隨着電器的改動不斷改動,同時每次新增一個電器,遙控器要進行相似「註冊」的行爲,須要將電器接入到遙控器,這樣顯然是不符合邏輯的,由於咱們都知道,遙控器是單純的指揮者,他不參與任何命令的操做細節,同時雖然真正工做的方法是具體對象的方法,可是這種形式相似將電器「塞」到了遙控器的內部執行,這樣也是存在問題,咱們下面須要修改一下這種嚴重耦合的設計。
咱們按照命令模式的結構圖,改寫案例,咱們須要定義下面的類和對應的接口:
+ RemoteControl 遙控器 + Command(接口) 命令規範接口,用於接入到遙控器內部 + LightCommandConcrete 控制電器的亮滅命令實現 + SwitchCommandConcrete 控制電器的開關命令實現 + Light 燈泡 + Television 電視機
首先,咱們定義命令的接口,定義接口的規範方法。而後定義實現子類實現不一樣命令的操做效果,在命令實現類的內部,咱們組合實際執行對象,在接口方法調用實際的對象方法,這樣就作到了執行者和發送者之間的解耦。
接着,咱們改寫控制器,他不在持有任何實際的對象方法,經過組合命令的接口,讓客戶端傳入實現的功能,經過這種方式,遙控器不在須要依賴具體的電器實現調用具體方法,而是關注命令的接口方法,一切的細節都在命令的子類內部。
下面代碼是依照命令模式進行的最簡單的一個實現。
// 命令接口 public interface Command { /** * 接口備份 */ void execute(); } public class LightCommandConcrete implements Command { private Light light = new Light(); @Override public void execute() { light.on(); } } public class SwitchCommandConcrete implements Command{ private Television television = new Television(); @Override public void execute() { television.on(); } } // 遙控器 public class RemoteControl { private Command command; public RemoteControl(Command command) { this.command = command; } public void execute(){ command.execute(); } public Command getCommand() { return command; } public void setCommand(Command command) { this.command = command; } } public class Main { public static void main(String[] args) { RemoteControl remoteControl = new RemoteControl(new LightCommandConcrete()); remoteControl.execute(); remoteControl.setCommand(new SwitchCommandConcrete()); remoteControl.execute(); } }
通過上面的代碼改造,咱們成功上面的代碼改造爲命令模式的代碼,使用設計模式以後,咱們將調用者和實際執行者進行了解耦,控制器不須要知道執行的細節,只須要組合本身的命令接口,由客戶端指定但願實現的內容,執行相對應的具體命令。
下面是對應案例如何進行後續的擴展,對於這部份內容文章篇幅有限,同時本着不重複造輪子的理念,請閱讀《Head First設計模式》關於命令模式這一個章節,同時安利一下這本書,很是通俗易懂的講解設計模式,對於我的的提高幫助很大。
Undo是一個很常見的功能,若是想要讓Undo的操做集成到案例內部,須要按照以下的步驟進行操做:
Undo
的操做,讓全部命令支持undo
undo()
方法調用,而且根據實際的組合對象調用方法undo()
操做的具體行爲效果。在命令的實現類內部,須要增長一個最後變量值的記錄,用於記錄當前最後一步操做的屬性和變量
實現一個命令類,經過組合數組或者堆棧組合多個其餘命令對象,經過for循環的形式依次調用。
undo也可使用這種方式進行調用的,可是要注意**調用的順序相反
優勢:
缺點:
命令模式是一種很是常見的設計模式,這種模式更多的關注點是解耦請求的發送方和實現方,命令模式在系統設計中使用仍是十分常見的,是一種值得關注的設計模式。