一.什麼是命令模式?java
命令模式,封裝了方法調用細節,以解耦請求者與執行者,具體流程以下:數據庫
1.從請求者(客戶)的角度看編輯器
請求者(客戶)發出請求 -> 調用者(系統)構造命令對象封裝請求 -> 調用者調用命令對象的指定方法(請求被執行)ide
很明顯,請求者根本不知道執行者是誰,更不知道具體執行細節。固然請求者自己也並不關心這些,它只要知道請求被執行了就好。測試
2.從執行者(低層組件)的角度看spa
執行者(低層組件)被調用 -> 執行者調用內部方法(請求被執行)線程
一樣的,執行者根本不知道請求者是誰,甚至不清楚調用者,不過不要緊,執行者只要本本分分的作好本職工做就行了,不必知道領導的狀況。日誌
3.從調用者(系統)的角度看對象
接到請求 -> 建立命令對象封裝請求 -> 在適當的時候調用命令對象的動做來執行請求(請求被執行)blog
調用者不知道執行者是誰,也不清楚請求者,它只負責構造命令並控制命令被執行,這就足夠了。
從上面能夠看出各個對象之間的低耦合關係:
請求者(客戶)與執行者(低層組件)被完全解耦,做爲中間人的調用者也不瞭解請求者與執行者的具體細節,它們被很好的保護了起來
這正是咱們想要的。
二.舉個例子
現實世界中任何一個稍微複雜的子系統都應當有一套命令,好比餐館的運行機制:
顧客A來到餐館點一碗麪(發出請求) -> 櫃檯服務員記錄下來(建立命令) -> 服務員把小票扔給廚房 -> 廚師C很快作好了一碗麪(請求被執行)
顧客不知道將由誰來作這碗麪,櫃檯服務員也不知道,廚師不知道是誰點了這碗麪,只知道作完面就能夠休息了
是否是與命令模式很相像?
不妨用代碼來實現上面的機制
首先,咱們須要一個命令接口,畢竟命令纔是命令模式的核心,沒有命令,一切都是空想
package CommandPattern; /** * @author ayqy * 定義Command接口 */ public interface Command { public abstract void execute();//只須要定義一個統一的執行方法 }
有了命令還須要執行者,不然只有將軍沒有小兵,餐館的執行者固然是廚師:
package CommandPattern; /** * @author ayqy * 定義Chef基類 */ public abstract class Chef { //在此定義廚師的公共屬性 /** * 定義烹飪方法 */ public abstract void cook(); //在此定義其它有用的方法 }
咱們還須要實現具體的廚師,術業有專攻:
作面的廚師:
package CommandPattern; /** * @author ayqy * 定義專業作面的廚師 */ public class NoodlesChef extends Chef{ @Override public void cook() { System.out.println("作好了一碗美味的拉麪"); } }
作餅的廚師:
package CommandPattern; /** * @author ayqy * 定義專業作餅的廚師 */ public class PieChef extends Chef{ @Override public void cook() { System.out.println("作好了一塊香噴噴的大餅"); } }
有了小兵,有了將軍,咱們還須要一套完整的命令:
package CommandPattern; /** * @author ayqy * 實現具體NoodlesCommand */ public class NoodlesCommand implements Command{ private NoodlesChef chef;//專業作面的廚師 public NoodlesCommand(){ chef = new NoodlesChef(); } @Override public void execute() { chef.cook(); //調用其它須要的方法 } }
package CommandPattern; /** * @author ayqy * 實現具體PieCommand */ public class PieCommand implements Command{ private PieChef chef;//專業作餅的廚師 public PieCommand(){ chef = new PieChef(); } @Override public void execute() { chef.cook(); //調用其它須要的方法 } }
準備工做作好了,餐館能夠開張了
三.效果示例
須要一個Test類:
package CommandPattern; /** * @author ayqy * 實現測試類 */ public class Test { public static void main(String[] args) { System.out.println("Command Pattern餐館開張。。"); System.out.println("第一位客戶X先生"); System.out.println("X先生:你好,我須要一碗麪,我餓極了"); NoodlesCommand nCmd = new NoodlesCommand(); System.out.println("櫃檯服務員:好的,我已經記下了,立刻就好"); System.out.println("櫃檯服務員:廚房~~,接單"); nCmd.execute(); System.out.println("X先生:真快啊!"); System.out.println(); System.out.println("第二位客戶XX先生"); System.out.println("XX先生:你好,我須要一塊餅,20分鐘後來取"); PieCommand pCmd = new PieCommand(); System.out.println("櫃檯服務員:好的,我已經記下了"); System.out.println("15分鐘後"); System.out.println("櫃檯服務員:廚房~~,接單"); pCmd.execute(); System.out.println("XX先生:真準時啊!"); } }
結果示例:
從例子能夠看出:
1.調用者(櫃檯服務員)能夠控制具體執行時機,但對具體執行者(廚師)的細節徹底不清楚
2.請求者(顧客)徹底不知道餐館的運行機制,不知道點的餐是廚師作的仍是服務員作的或者是從隔壁買的。。
3.執行者(廚師)徹底不知道請求者的狀況,它只作了本職工做,其它的什麼都不知道
四.命令模式的擴展
1.宏命令(多條命令順序執行)
咱們能夠定義「命令的命令」來實現(這種特殊的命令的execute方法內部是順序調用其它若干命令的execute方法。。)
2.撤銷
假如來了不少顧客,點了不少份餐點,過了一下子有幾個顧客等不及了須要撤銷,咱們如何實現?
維護一個命令列表,記錄已經建立的命令,撤銷時須要找到對應的命令,執行撤銷操做
固然,前提是命令對象支持撤銷,咱們須要作一些修改:
package CommandPattern; /** * @author ayqy * 定義Command接口 */ public interface Command { public abstract void execute();//只須要定義一個統一的執行方法 public abstract void undo();//定義統一的撤銷方法 }
各個命令的撤銷操做可能不一樣,所以定義爲抽象方法,由子類來實現具體操做
*如何支持多步順序撤銷?
餐館的例子可能不須要這樣的功能,不妨想一想另外一個情景,文本編輯器
用戶發出了一系列命令,完成了一些列操做,後來發現並不須要這樣作,用戶會撤銷修改(Ctrl + Z),這時咱們須要執行相反的操做對內容做以還原,要如何實現?
仍是要先實現各個命令的undo行爲(執行與execute相反順序的操做便可),除此以外,咱們還須要一個棧來記錄已經被執行過的操做,以支持撤銷到初始狀態
3.隊列請求
咱們能夠創建一個工做線程,負責全部運算,想象有一個通道,輸入是一條條不一樣命令,輸出是命令的執行結果
可能上一刻工做線程在作大餅,下一刻已經出去買菜了。。
*這樣作有什麼好處?
能夠把運算限制在指定的幾個線程中,加以控制
4.日誌請求
多用於數據庫管理系統的實現,咱們須要把一系列的操做記錄下來(如寫在硬盤上),在遇到系統故障時讀出來以恢復數據,如何實現?
利用對象的序列化把對象保存起來(記錄日誌),在須要的時候反序列化(恢復事務)
五.總結
命令模式能夠有效地解耦請求者與執行者,還能夠提供一些額外的好處(好比支持撤銷操做、隊列請求、記錄日誌等等)