設計模式 命令模式 之 管理智能家電

繼續設計模式哈,今天帶來命令模式,二話不說,先看定義:

定義:將“請求”封裝成對象,以便使用不同的請求、隊列或者日誌來參數化其他對象。命令模式也支持可撤銷的操作。

這尼瑪定義,看得人蛋疼,看不明白要淡定,我稍微簡化一下:將請求封裝成對象,將動作請求者和動作執行者解耦。好了,直接用例子來說明。

需求:最近智能家電很火熱啊,未來尼瑪估計冰箱都會用支付寶自動買東西了,,,,假設現在有電視、電腦、電燈等家電,現在需要你做個遙控器控制所有家電的開關,要求做到每個按鈕對應的功能供用戶個性化,對於新買入家電要有非常強的擴展性。

這個需求一看,尼瑪要是沒有什麼個性化、擴展性還好說啊,直接針對每個遙控器的按鈕onClick,然後在裏面把代碼寫死就搞定了,但是個性化怎麼整,還要有擴展性。。。

好了,下面命令模式出場,命令模式的核心就是把命令封裝成類,對於命令執行者不需要知道現在執行的具體是什麼命令。

1、首先看下我們擁有的家電的API:

  1. package com.zhy.pattern.command;  
  2. /** 
  3.  * 門 
  4.  * @author zhy 
  5.  * 
  6.  */  
  7. public class Door  
  8. {  
  9.     public void open()  
  10.     {  
  11.         System.out.println("打開門");  
  12.     }  
  13.   
  14.     public void close()  
  15.     {  
  16.         System.out.println("關閉門");  
  17.     }  
  18.   
  19. }  

  1. package com.zhy.pattern.command;  
  2.   
  3. /** 
  4.  * 電燈 
  5.  * @author zhy 
  6.  * 
  7.  */  
  8. public class Light  
  9. {  
  10.     public void on()  
  11.     {  
  12.         System.out.println("打開電燈");  
  13.     }  
  14.   
  15.     public void off()  
  16.     {  
  17.         System.out.println("關閉電燈");  
  18.     }  
  19. }  

  1. package com.zhy.pattern.command;  
  2. /** 
  3.  * 電腦 
  4.  * @author zhy 
  5.  * 
  6.  */  
  7. public class Computer  
  8. {  
  9.     public void on()  
  10.     {  
  11.         System.out.println("打開電腦");  
  12.     }  
  13.       
  14.     public void off()  
  15.     {  
  16.         System.out.println("關閉電腦");  
  17.     }  
  18. }  

看來我們有電燈、電腦、和門,並且開關的接口的設計好了。接下來看如何把命令封裝成類:
  1. package com.zhy.pattern.command;  
  2.   
  3. public interface Command  
  4. {  
  5.     public void execute();  
  6. }  


  1. package com.zhy.pattern.command;  
  2.   
  3. /** 
  4.  * 關閉電燈的命令 
  5.  * @author zhy 
  6.  * 
  7.  */  
  8. public class LightOffCommond implements Command  
  9. {  
  10.     private Light light ;   
  11.       
  12.     public LightOffCommond(Light light)  
  13.     {  
  14.         this.light = light;  
  15.     }  
  16.   
  17.     @Override  
  18.     public void execute()  
  19.     {  
  20.         light.off();  
  21.     }  
  22.   
  23. }  

  1. package com.zhy.pattern.command;  
  2.   
  3. /** 
  4.  * 打開電燈的命令 
  5.  * @author zhy 
  6.  * 
  7.  */  
  8. public class LightOnCommond implements Command  
  9. {  
  10.     private Light light ;   
  11.       
  12.     public LightOnCommond(Light light)  
  13.     {  
  14.         this.light = light;  
  15.     }  
  16.   
  17.     @Override  
  18.     public void execute()  
  19.     {  
  20.         light.on();  
  21.     }  
  22.   
  23. }  

  1. package com.zhy.pattern.command;  
  2.   
  3. /** 
  4.  * 開電腦的命令 
  5.  * @author zhy 
  6.  * 
  7.  */  
  8. public class ComputerOnCommond implements Command  
  9. {  
  10.     private Computer computer ;   
  11.       
  12.     public ComputerOnCommond( Computer computer)  
  13.     {  
  14.         this.computer = computer;  
  15.     }  
  16.   
  17.     @Override  
  18.     public void execute()  
  19.     {  
  20.         computer.on();  
  21.     }  
  22.   
  23. }  

  1. package com.zhy.pattern.command;  
  2.   
  3. /** 
  4.  * 關電腦的命令 
  5.  * @author zhy 
  6.  * 
  7.  */  
  8. public class ComputerOffCommond implements Command  
  9. {  
  10.     private Computer computer ;   
  11.       
  12.     public ComputerOffCommond( Computer computer)  
  13.     {  
  14.         this.computer = computer;  
  15.     }  
  16.   
  17.     @Override  
  18.     public void execute()  
  19.     {  
  20.         computer.off();  
  21.     }  
  22.       
  23.       
  24.   
  25. }  

好了,不貼那麼多了,既然有很多命令,按照設計原則,我們肯定有個超類型的Command,然後各個子類,看我們把每個命令(請求)都封裝成類了。接下來看我們的遙控器。
  1. package com.zhy.pattern.command;  
  2.   
  3. /** 
  4.  * 控制器面板,一共有9個按鈕 
  5.  *  
  6.  * @author zhy 
  7.  *  
  8.  */  
  9. public class ControlPanel  
  10. {  
  11.     private static final int CONTROL_SIZE = 9;  
  12.     private Command[] commands;  
  13.   
  14.     public ControlPanel()  
  15.     {  
  16.         commands = new Command[CONTROL_SIZE];  
  17.         /** 
  18.          * 初始化所有按鈕指向空對象 
  19.          */  
  20.         for (int i = 0; i < CONTROL_SIZE; i++)  
  21.         {  
  22.             commands[i] = new NoCommand();  
  23.         }  
  24.     }  
  25.   
  26.     /** 
  27.      * 設置每個按鈕對應的命令 
  28.      * @param index 
  29.      * @param command 
  30.      */  
  31.     public void setCommand(int index, Command command)  
  32.     {  
  33.         commands[index] = command;  
  34.     }  
  35.   
  36.     /** 
  37.      * 模擬點擊按鈕 
  38.      * @param index 
  39.      */  
  40.     public void keyPressed(int index)  
  41.     {  
  42.         commands[index].execute();  
  43.     }  
  44.   
  45. }  

  1. package com.zhy.pattern.command;  
  2.   
  3. /** 
  4.  * @author zhy 
  5.  * 
  6.  */  
  7. public class NoCommand implements Command  
  8. {  
  9.     @Override  
  10.     public void execute()  
  11.     {  
  12.   
  13.     }  
  14.   
  15. }  

注意看到我們的遙控器有9個按鈕,提供了設置每個按鈕的功能和點擊的方法,還有注意到我們使用了一個NoCommand對象,叫做空對象,這個對象的好處就是,我們不用執行前都判斷個if(!=null),並且提供了一致的操作。

最後測試一下代碼:

  1. package com.zhy.pattern.command;  
  2.   
  3. public class Test  
  4. {  
  5.     public static void main(String[] args)  
  6.     {  
  7.         /** 
  8.          * 三個家電 
  9.          */  
  10.         Light light = new Light();  
  11.         Door door = new Door();  
  12.         Computer computer = new Computer();  
  13.         /** 
  14.          * 一個控制器,假設是我們的app主界面 
  15.          */  
  16.         ControlPanel controlPanel = new ControlPanel();  
  17.         // 爲每個按鈕設置功能  
  18.         controlPanel.setCommand(0new LightOnCommond(light));  
  19.         controlPanel.setCommand(1new LightOffCommond(light));  
  20.         controlPanel.setCommand(2new ComputerOnCommond(computer));  
  21.         controlPanel.setCommand(3new ComputerOffCommond(computer));  
  22.         controlPanel.setCommand(4new DoorOnCommond(door));  
  23.         controlPanel.setCommand(5new DoorOffCommond(door));  
  24.   
  25.         // 模擬點擊  
  26.         controlPanel.keyPressed(0);  
  27.         controlPanel.keyPressed(2);  
  28.         controlPanel.keyPressed(3);  
  29.         controlPanel.keyPressed(4);  
  30.         controlPanel.keyPressed(5);  
  31.         controlPanel.keyPressed(8);// 這個沒有指定,但是不會出任何問題,我們的NoCommand的功勞  
  32.   
  33.           
  34.   
  35.     }  
  36. }  

輸出結果:


可以看到任意按鈕可以隨意配置任何命令,再也不需要尼瑪的變一下需求改代碼了,隨便用戶怎麼個性化了。其實想白了,這裏的設置我們還可以配置到一個配置文件中,完全的解耦有木有。


好了,用戶對於這個按鈕可能還不是太滿意,用戶希望夜深人靜的時候,能夠提供個按鈕直接關門、關燈、開電腦,,,,大家懂的,,,我們稍微修改下代碼,滿足他

定義一個命令,用戶幹一些列的事,可配置,且與原來的命令保持接口一致:

  1. package com.zhy.pattern.command;  
  2.   
  3. /** 
  4.  * 定義一個命令,可以幹一系列的事情 
  5.  *  
  6.  * @author zhy 
  7.  *  
  8.  */  
  9. public class QuickCommand implements Command  
  10. {  
  11.     private Command[] commands;  
  12.   
  13.     public QuickCommand(Command[] commands)  
  14.     {  
  15.         this.commands = commands;  
  16.     }  
  17.   
  18.     @Override  
  19.     public void execute()  
  20.     {  
  21.         for (int i = 0; i < commands.length; i++)  
  22.         {  
  23.             commands[i].execute();  
  24.         }  
  25.     }  
  26.   
  27. }  

好了,已經滿足屌絲的需求了。我們測試看看。
  1. // 定義一鍵搞定模式  
  2.         QuickCommand quickCommand = new QuickCommand(new Command[] { new DoorOffCommond(door),  
  3.                 new LightOffCommond(light), new ComputerOnCommond(computer) });  
  4.         System.out.println("****點擊一鍵搞定按鈕****");  
  5.         controlPanel.setCommand(8, quickCommand);  
  6.         controlPanel.keyPressed(8);  


是不是很完美。


最後,繼續來談談命令模式,命令模式就是把命令封裝成對象,然後將動作請求者與動作執行者完全解耦,上例中遙控器的按鈕和電器一毛錢關係都沒吧。

還記得定義中提到了隊列,命令模式如何用於隊列呢,比如飯店有很多個點菜的地方,有一個做菜的地方,把點菜看作命令,做菜看作命令執行者,不斷有人點菜就相當於把菜加入隊列,對於做菜的只管從隊列裏面取,取一個做一個。

定義中還提到了日誌,日誌一般用於記錄用戶行爲,或者在異常時恢復時用的,比如每個命令現在包含兩個方法,一個執行execute,一個undo(上例中爲了方便大家理解,沒有寫undo),我們可以把用戶所有命令調用保存到日誌中,比如用戶操作不當了,電器異常了,只需要把日誌中所有的命令拿出來執行一遍undo就完全恢復了,是吧,就是這麼個意思。


好了,各位留個言、點個贊算是對我的支持,多謝大家~

發佈了66 篇原創文章 · 獲贊 13 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章