23種設計模式之命令模式1

3.1  認識命令模式

(1)命令模式的關鍵 
        命令模式的關鍵之處就是把請求封裝成爲對象,也就是命令對象,並定義了統一的執行操作的接口,這個命令對象可以被存儲、轉發、記錄、處理、撤銷等,整個命令模式都是圍繞這個對象在進行。 
(2)命令模式的組裝和調用
        在命令模式中經常會有一個命令的組裝者,用它來維護命令的“虛”實現和真實實現之間的關係。如果是超級智能的命令,也就是說命令對象自己完全實現好了,不需要接收者,那就是命令模式的退化,不需要接收者,自然也不需要組裝者了。 
而真正的用戶就是具體化請求的內容,然後提交請求進行觸發就好了。真正的用戶會通過invoker來觸發命令。 
        在實際開發過程中,Client和Invoker可以融合在一起,由客戶在使用命令模式的時候,先進行命令對象和接收者的組裝,組裝完成後,就可以調用命令執行請求。 
(3)命令模式的接收者
        接收者可以是任意的類,對它沒有什麼特殊要求,這個對象知道如何真正執行命令的操作,執行時是從command的實現類裏面轉調過來。 
        一個接收者對象可以處理多個命令,接收者和命令之間沒有約定的對應關係。接收者提供的方法個數、名稱、功能和命令中的可以不一樣,只要能夠通過調用接收者的方法來實現命令對應的功能就可以了。 
(4)智能命令
        在標準的命令模式裏面,命令的實現類是沒有真正實現命令要求的功能的,真正執行命令的功能的是接收者。 
        如果命令的實現對象比較智能,它自己就能真實地實現命令要求的功能,而不再需要調用接收者,那麼這種情況就稱爲智能命令。 
        也可以有半智能的命令,命令對象知道部分實現,其它的還是需要調用接收者來完成,也就是說命令的功能由命令對象和接收者共同來完成。 
(5)發起請求的對象和真正實現的對象是解耦的
        請求究竟由誰處理,如何處理,發起請求的對象是不知道的,也就是發起請求的對象和真正實現的對象是解耦的。發起請求的對象只管發出命令,其它的就不管了。 
(6)命令模式的調用順序示意圖
        使用命令模式的過程分成兩個階段,一個階段是組裝命令對象和接收者對象的過程,另外一個階段是觸發調用Invoker,來讓命令真正執行的過程。 
        先看看組裝過程的調用順序示意圖,如圖4所示: 

 
                        圖4  命令模式組裝過程的調用順序示意圖 
接下來再看看真正執行命令時的調用順序示意圖,如圖5所示: 

 
                           圖5  命令模式執行過程的調用順序示意圖

3.2  參數化配置

 
        所謂命令模式的參數化配置,指的是:可以用不同的命令對象,去參數化配置客戶的請求。 
        像前面描述的那樣:客戶按下一個按鈕,到底是開機還是重啓,那要看參數化配置的是哪一個具體的按鈕對象,如果參數化的是開機的命令對象,那就執行開機的功能,如果參數化的是重啓的命令對象,那就執行重啓的功能。雖然按下的是同一個按鈕,相當於是同一個請求,但是爲請求配置不同的按鈕對象,那就會執行不同的功能。 
把這個功能用代碼實現出來,一起來體會一下命令模式的參數化配置。 
        (1)同樣先定義主板接口吧,現在想要添加一個重啓的按鈕,因此主板需要添加一個方法來實現重啓的功能,示例代碼如下:

 

java代碼:
  1. /** 
  2.  * 主板的接口 
  3.  */  
  4. public interface MainBoardApi {  
  5.     /** 
  6.      * 主板具有能開機的功能 
  7.      */  
  8.     public void open();  
  9.     /** 
  10.      * 主板具有實現重啓的功能 
  11.      */  
  12.     public void reset();  
  13. }  

 

 
    接口發生了改變,實現類也得有相應的改變,由於兩個主板的實現示意差不多,因此還是隻示例一個,示例代碼如下
 

 

java代碼:
  1. /** 
  2.  * 技嘉主板類,命令的真正實現者,在Command模式中充當Receiver 
  3.  */  
  4. public class GigaMainBoard implements MainBoardApi{  
  5.     /** 
  6.      * 真正的開機命令的實現 
  7.      */  
  8.     public void open(){  
  9.         System.out.println("技嘉主板現在正在開機,請等候");  
  10.         System.out.println("接通電源......");  
  11.         System.out.println("設備檢查......");  
  12.         System.out.println("裝載系統......");  
  13.         System.out.println("機器正常運轉起來......");  
  14.         System.out.println("機器已經正常打開,請操作");  
  15.     }  
  16.     /** 
  17.      * 真正的重新啓動機器命令的實現 
  18.      */  
  19.     public void reset(){  
  20.         System.out.println("技嘉主板現在正在重新啓動機器,請等候");  
  21.         System.out.println("機器已經正常打開,請操作");  
  22.     }  
  23. }  

 

 
 
(2)該來定義命令和按鈕了,命令接口沒有任何變化,原有的開機命令的實現也沒有任何變化,只是新添加了一個重啓命令的實現,示例代碼如下:
  
java代碼:
  1. /** 
  2.  * 重啓機器命令的實現,實現Command接口, 
  3.  * 持有重啓機器命令的真正實現,通過調用接收者的方法來實現命令 
  4.  */  
  5. public class ResetCommand implements Command{  
  6.     /** 
  7.      * 持有真正實現命令的接收者——主板對象 
  8.      */  
  9.     private MainBoardApi mainBoard = null;  
  10.     /** 
  11.      * 構造方法,傳入主板對象 
  12.      * @param mainBoard 主板對象 
  13.      */  
  14.     public ResetCommand(MainBoardApi mainBoard) {  
  15.         this.mainBoard = mainBoard;  
  16.     }  
  17.       
  18.     public void execute() {  
  19.         //對於命令對象,根本不知道如何重啓機器,會轉調主板對象  
  20.         //讓主板去完成重啓機器的功能  
  21.         this.mainBoard.reset();  
  22.     }  
  23. }  
 
(3)持有命令的機箱也需要修改,現在不只一個命令按鈕了,有兩個了,所以需要在機箱類裏面新添加重啓的按鈕,爲了簡單,沒有做成集合。示例代碼如下:

java代碼:
  1. /** 
  2.  * 機箱對象,本身有按鈕,持有按鈕對應的命令對象 
  3.  */  
  4. public class Box {  
  5.     private Command openCommand;  
  6.     public void setOpenCommand(Command command){  
  7.         this.openCommand = command;  
  8.     }  
  9.     public void openButtonPressed(){  
  10.         //按下按鈕,執行命令  
  11.         openCommand.execute();  
  12.     }  
  13.     /** 
  14.      * 重啓機器命令對象 
  15.      */  
  16.     private Command resetCommand;  
  17.     /** 
  18.      * 設置重啓機器命令對象 
  19.      * @param command  
  20.      */  
  21.     public void setResetCommand(Command command){  
  22.         this.resetCommand = command;  
  23.     }  
  24.     /** 
  25.      * 提供給客戶使用,接收並相應用戶請求,相當於重啓按鈕被按下觸發的方法 
  26.      */  
  27.     public void resetButtonPressed(){  
  28.         //按下按鈕,執行命令  
  29.         resetCommand.execute();  
  30.     }  
  31. }  
 
(4)看看客戶如何使用這兩個按鈕,示例代碼如下

 

java代碼:
  1. public class Client {  
  2.     public static void main(String[] args) {  
  3.         //1:把命令和真正的實現組合起來,相當於在組裝機器,  
  4.         //把機箱上按鈕的連接線插接到主板上。  
  5.         MainBoardApi mainBoard = new GigaMainBoard();  
  6.         //創建開機命令  
  7.         OpenCommand openCommand = new OpenCommand(mainBoard);  
  8.         //創建重啓機器的命令  
  9.         ResetCommand resetCommand = new ResetCommand(mainBoard);  
  10.         //2:爲機箱上的按鈕設置對應的命令,讓按鈕知道該幹什麼  
  11.         Box box = new Box();  
  12.         //先正確配置,就是開機按鈕對開機命令,重啓按鈕對重啓命令  
  13.         box.setOpenCommand(openCommand);  
  14.         box.setResetCommand(resetCommand);  
  15.           
  16.         //3:然後模擬按下機箱上的按鈕  
  17.         System.out.println("正確配置下------------------------->");  
  18.         System.out.println(">>>按下開機按鈕:>>>");  
  19.         box.openButtonPressed();  
  20.         System.out.println(">>>按下重啓按鈕:>>>");  
  21.         box.resetButtonPressed();  
  22.           
  23.         //然後來錯誤配置一回,反正是進行參數化配置  
  24.         //就是開機按鈕對重啓命令,重啓按鈕對開機命令  
  25.         box.setOpenCommand(resetCommand);  
  26.         box.setResetCommand(openCommand);  
  27.         //4:然後還是來模擬按下機箱上的按鈕  
  28.         System.out.println("錯誤配置下------------------------->");  
  29.         System.out.println(">>>按下開機按鈕:>>>");  
  30.         box.openButtonPressed();  
  31.         System.out.println(">>>按下重啓按鈕:>>>");  
  32.         box.resetButtonPressed();  
  33.     }  
  34. }  

 

 
運行一下看看,很有意思,結果如下:
  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章