命令模式(二)
宏命令
什麽是宏命令呢?簡單點說就是包含多個命令的命令,是一個命令的組合。舉個例子來說吧,設想一下你去飯店吃飯的過程:
(1)你走進一家飯店,找到座位坐下
(2)服務員走過來,遞給你菜譜
(3)你開始點菜,服務員開始記錄菜單,菜單是三聯的,點菜完畢,服務員就會把菜單分成三份,一份給後廚,一份給收銀臺,一份保留備查。
(4)點完菜,你坐在座位上等候,後廚會按照菜單做菜
(5)每做好一份菜,就會由服務員送到你桌子上
(6)然後你就可以大快朵頤了
事實上,到飯店點餐是一個很典型的命令模式應用,作為客戶的你,只需要發出命令,就是要吃什麽菜,每道菜就相當於一個命令對象,服務員會在菜單上記錄你點的菜,然後把菜單傳遞給後廚,後廚拿到菜單,會按照菜單進行飯菜制作,後廚就相當於接收者,是命令的真正執行者,廚師才知道每道菜具體怎麽實現。
同時呢,服務員還持有命令對象,也就是菜單,最後啟動命令執行的也是服務員。因此,服務員就相當於標準命令模式中的Client和Invoker的融合。
畫個圖來描述上述對應關系,如圖所示:
1:宏命令在哪裏?
仔細觀察上面的過程,再想想前面的命令模式的實現,看出點什麽沒有?
前面實現的命令模式,都是客戶端發出一個命令,然後馬上就執行了這個命令,但是在上面的描述裏面呢?是點一個菜,服務員就告訴廚師,然後廚師就開始做嗎?很明顯不是的,服務員會一直等,等到你點完菜,當你說“點完了”的時候,服務員才會啟動命令的執行,請註意,這個時候執行的就不是一個命令了,而是執行一堆命令。
描述這一堆命令的就是菜單,如果把菜單也抽象成為一個命令,就相當於一個大的命令,當客戶說“點完了”的時候,就相當於觸發這個大的命令,意思就是執行菜單這個命令就可以了,這個菜單命令包含多個命令對象,一個命令對象就相當於一道菜。
那麽這個菜單就相當於我們說的宏命令。
2:如何實現宏命令
宏命令從本質上講類似於一個命令,基本上把它當命令對象進行處理。但是它跟普通的命令對象又有些不一樣,就是宏命令包含有多個普通的命令對象,執行一個宏命令,簡單點說,就是執行宏命令裏面所包含的所有命令對象,有點打包執行的意味。
(1)先來定義接收者,就是廚師的接口和實現,先看接口,示例代碼如下:
/** * 廚師的接口 */ public interface CookApi { /** * 示意,做菜的方法 * @param name 菜名 */ public void cook(String name); }
廚師又分成兩類,一類是做熱菜的師傅,一類是做涼菜的師傅,先看看做熱菜的廚師的實現示意,示例代碼如下:
/** * 廚師對象,做熱菜 */ public class HotCook implements CookApi{ public void cook(String name) { System.out.println("本廚師正在做:"+name); } }
做涼菜的師傅,示例代碼如下:
/** * 廚師對象,做涼菜 */ public class CoolCook implements CookApi { public void cook(String name) { System.out.println("涼菜"+name+"已經做好,本廚師正在裝盤。" ); } }
(2)接下來,來定義命令接口,跟以前一樣,示例代碼如下:
/** * 命令接口,聲明執行的操作 */ public interface Command { /** * 執行命令對應的操作 */ public void execute(); }
(3)定義好了命令的接口,該來具體實現命令了。
實現方式跟以前一樣,持有接收者,當執行命令的時候,轉調接收者,讓接收者去真正實現功能,這裏的接收者就是廚師。
這裏實現命令的時候,跟標準的命令模式的命令實現有一點不同,標準的命令模式的命令實現的時候,是通過構造方法傳入接收者對象,這裏改成了使用setter的方式來設置接收者對象,也就是說可以動態的切換接收者對象,而無須重新構建對象。
示例中定義了三道菜,分別是兩道熱菜:北京烤鴨、綠豆排骨煲,一道涼菜:蒜泥白肉,三個具體的實現類非常類似,只是菜名不同,為了節省篇幅,這裏就只看一個命令對象的具體實現。代碼示例如下:
/** * 命令對象,綠豆排骨煲 */ public class ChopCommand implements Command{ /** * 持有具體做菜的廚師的對象 */ private CookApi cookApi = null; /** * 設置具體做菜的廚師的對象 * @param cookApi 具體做菜的廚師的對象 */ public void setCookApi(CookApi cookApi) { this.cookApi = cookApi; } public void execute() { this.cookApi.cook("綠豆排骨煲"); } }
/** * 命令對象,北京烤鴨 */ public class DuckCommand implements Command{ private CookApi cookApi = null; public void setCookApi(CookApi cookApi) { this.cookApi = cookApi; } public void execute() { this.cookApi.cook("北京烤鴨"); } }
/** * 命令對象,蒜泥白肉 */ public class PorkCommand implements Command { private CookApi cookApi = null; public void setCookApi(CookApi cookApi) { this.cookApi = cookApi; } public void execute() { this.cookApi.cook("蒜泥白肉"); } }
(4)該來組合菜單對象了,也就是宏命令對象。
首先宏命令就其本質還是一個命令,所以一樣要實現Command接口
其次宏命令跟普通命令的不同在於:宏命令是多個命令組合起來的,因此在宏命令對象裏面會記錄多個組成它的命令對象
第三,既然是包含多個命令對象,得有方法讓這多個命令對象能被組合進來
第四,既然宏命令包含了多個命令對象,執行宏命令對象就相當於依次執行這些命令對象,也就是循環執行這些命令對象
看看代碼示例會更清晰些,代碼示例如下:
/** * 菜單對象,是個宏命令對象 */ public class MenuCommand implements Command { /** * 用來記錄組合本菜單的多道菜品,也就是多個命令對象 */ private Collection<Command> col = new ArrayList<Command>(); /** * 點菜,把菜品加入到菜單中 * @param cmd 客戶點的菜 */ public void addCommand(Command cmd){ col.add(cmd); } public void execute() { //執行菜單其實就是循環執行菜單裏面的每個菜 for(Command cmd : col){ cmd.execute(); } } }
(5)該服務員類重磅登場了,它實現的功能,相當於標準命令模式實現中的Client加上Invoker,前面都是文字講述,看看代碼如何實現,示例代碼如下:
/** * 服務員,負責組合菜單,負責組裝每個菜和具體的實現者, * 還負責執行調用,相當於標準Command模式的Client+Invoker */ public class Waiter { /** * 持有一個宏命令對象——菜單 */ private MenuCommand menuCommand = new MenuCommand(); /** * 客戶點菜 * @param cmd 客戶點的菜,每道菜是一個命令對象 */ public void orderDish(Command cmd){ //客戶傳過來的命令對象是沒有和接收者組裝的 //在這裏組裝吧 CookApi hotCook = new HotCook(); CookApi coolCook = new CoolCook(); //判讀到底是組合涼菜師傅還是熱菜師傅 //簡單點根據命令的原始對象的類型來判斷 if(cmd instanceof DuckCommand){ ((DuckCommand)cmd).setCookApi(hotCook); }else if(cmd instanceof ChopCommand){ ((ChopCommand)cmd).setCookApi(hotCook); }else if(cmd instanceof PorkCommand){ //這是個涼菜,所以要組合涼菜的師傅 ((PorkCommand)cmd).setCookApi(coolCook); } //添加到菜單中 menuCommand.addCommand(cmd); } /** * 客戶點菜完畢,表示要執行命令了,這裏就是執行菜單這個組合命令 */ public void orderOver(){ this.menuCommand.execute(); } }
(6)費了這麽大力氣,終於可以坐下來歇息一下,點菜吃飯吧,一起來看看客戶端怎麽使用這個宏命令,其實在客戶端非常簡單,根本看不出宏命令來,代碼示例如下:
public class Client { public static void main(String[] args) { //客戶只是負責向服務員點菜就好了 //創建服務員 Waiter waiter = new Waiter(); //創建命令對象,就是要點的菜 Command chop = new ChopCommand(); Command duck = new DuckCommand(); Command pork = new PorkCommand(); //點菜,就是把這些菜讓服務員記錄下來 waiter.orderDish(chop); waiter.orderDish(duck); waiter.orderDish(pork); //點菜完畢 waiter.orderOver(); } }
運行一下,享受一下成果,結果如下:
本廚師正在做:綠豆排骨煲 本廚師正在做:北京烤鴨 涼菜蒜泥白肉已經做好,本廚師正在裝盤。
轉載至:http://sishuok.com/forum/blogPost/list/99.html
cc老師的設計模式是我目前看過最詳細最有實踐的教程。
本文出自 “ciyo技術分享” 博客,請務必保留此出處http://ciyorecord.blog.51cto.com/6010867/1945510
命令模式(二)