3.1 認識命令模式
(1)命令模式的關鍵
命令模式的關鍵之處就是把請求封裝成爲對象,也就是命令對象,並定義了統一的執行操作的接口,這個命令對象可以被存儲、轉發、記錄、處理、撤銷等,整個命令模式都是圍繞這個對象在進行。
(2)命令模式的組裝和調用
在命令模式中經常會有一個命令的組裝者,用它來維護命令的“虛”實現和真實實現之間的關係。如果是超級智能的命令,也就是說命令對象自己完全實現好了,不需要接收者,那就是命令模式的退化,不需要接收者,自然也不需要組裝者了。
而真正的用戶就是具體化請求的內容,然後提交請求進行觸發就好了。真正的用戶會通過invoker來觸發命令。
在實際開發過程中,Client和Invoker可以融合在一起,由客戶在使用命令模式的時候,先進行命令對象和接收者的組裝,組裝完成後,就可以調用命令執行請求。
(3)命令模式的接收者
接收者可以是任意的類,對它沒有什麼特殊要求,這個對象知道如何真正執行命令的操作,執行時是從command的實現類裏面轉調過來。
一個接收者對象可以處理多個命令,接收者和命令之間沒有約定的對應關係。接收者提供的方法個數、名稱、功能和命令中的可以不一樣,只要能夠通過調用接收者的方法來實現命令對應的功能就可以了。
(4)智能命令
在標準的命令模式裏面,命令的實現類是沒有真正實現命令要求的功能的,真正執行命令的功能的是接收者。
如果命令的實現對象比較智能,它自己就能真實地實現命令要求的功能,而不再需要調用接收者,那麼這種情況就稱爲智能命令。
也可以有半智能的命令,命令對象知道部分實現,其它的還是需要調用接收者來完成,也就是說命令的功能由命令對象和接收者共同來完成。
(5)發起請求的對象和真正實現的對象是解耦的
請求究竟由誰處理,如何處理,發起請求的對象是不知道的,也就是發起請求的對象和真正實現的對象是解耦的。發起請求的對象只管發出命令,其它的就不管了。
(6)命令模式的調用順序示意圖
使用命令模式的過程分成兩個階段,一個階段是組裝命令對象和接收者對象的過程,另外一個階段是觸發調用Invoker,來讓命令真正執行的過程。
先看看組裝過程的調用順序示意圖,如圖4所示:
圖4 命令模式組裝過程的調用順序示意圖
接下來再看看真正執行命令時的調用順序示意圖,如圖5所示:
圖5 命令模式執行過程的調用順序示意圖
命令模式的關鍵之處就是把請求封裝成爲對象,也就是命令對象,並定義了統一的執行操作的接口,這個命令對象可以被存儲、轉發、記錄、處理、撤銷等,整個命令模式都是圍繞這個對象在進行。
(2)命令模式的組裝和調用
在命令模式中經常會有一個命令的組裝者,用它來維護命令的“虛”實現和真實實現之間的關係。如果是超級智能的命令,也就是說命令對象自己完全實現好了,不需要接收者,那就是命令模式的退化,不需要接收者,自然也不需要組裝者了。
而真正的用戶就是具體化請求的內容,然後提交請求進行觸發就好了。真正的用戶會通過invoker來觸發命令。
在實際開發過程中,Client和Invoker可以融合在一起,由客戶在使用命令模式的時候,先進行命令對象和接收者的組裝,組裝完成後,就可以調用命令執行請求。
(3)命令模式的接收者
接收者可以是任意的類,對它沒有什麼特殊要求,這個對象知道如何真正執行命令的操作,執行時是從command的實現類裏面轉調過來。
一個接收者對象可以處理多個命令,接收者和命令之間沒有約定的對應關係。接收者提供的方法個數、名稱、功能和命令中的可以不一樣,只要能夠通過調用接收者的方法來實現命令對應的功能就可以了。
(4)智能命令
在標準的命令模式裏面,命令的實現類是沒有真正實現命令要求的功能的,真正執行命令的功能的是接收者。
如果命令的實現對象比較智能,它自己就能真實地實現命令要求的功能,而不再需要調用接收者,那麼這種情況就稱爲智能命令。
也可以有半智能的命令,命令對象知道部分實現,其它的還是需要調用接收者來完成,也就是說命令的功能由命令對象和接收者共同來完成。
(5)發起請求的對象和真正實現的對象是解耦的
請求究竟由誰處理,如何處理,發起請求的對象是不知道的,也就是發起請求的對象和真正實現的對象是解耦的。發起請求的對象只管發出命令,其它的就不管了。
(6)命令模式的調用順序示意圖
使用命令模式的過程分成兩個階段,一個階段是組裝命令對象和接收者對象的過程,另外一個階段是觸發調用Invoker,來讓命令真正執行的過程。
先看看組裝過程的調用順序示意圖,如圖4所示:
圖4 命令模式組裝過程的調用順序示意圖
接下來再看看真正執行命令時的調用順序示意圖,如圖5所示:
圖5 命令模式執行過程的調用順序示意圖
3.2 參數化配置
所謂命令模式的參數化配置,指的是:可以用不同的命令對象,去參數化配置客戶的請求。
像前面描述的那樣:客戶按下一個按鈕,到底是開機還是重啓,那要看參數化配置的是哪一個具體的按鈕對象,如果參數化的是開機的命令對象,那就執行開機的功能,如果參數化的是重啓的命令對象,那就執行重啓的功能。雖然按下的是同一個按鈕,相當於是同一個請求,但是爲請求配置不同的按鈕對象,那就會執行不同的功能。
把這個功能用代碼實現出來,一起來體會一下命令模式的參數化配置。
(1)同樣先定義主板接口吧,現在想要添加一個重啓的按鈕,因此主板需要添加一個方法來實現重啓的功能,示例代碼如下:
像前面描述的那樣:客戶按下一個按鈕,到底是開機還是重啓,那要看參數化配置的是哪一個具體的按鈕對象,如果參數化的是開機的命令對象,那就執行開機的功能,如果參數化的是重啓的命令對象,那就執行重啓的功能。雖然按下的是同一個按鈕,相當於是同一個請求,但是爲請求配置不同的按鈕對象,那就會執行不同的功能。
把這個功能用代碼實現出來,一起來體會一下命令模式的參數化配置。
(1)同樣先定義主板接口吧,現在想要添加一個重啓的按鈕,因此主板需要添加一個方法來實現重啓的功能,示例代碼如下:
java代碼:
接口發生了改變,實現類也得有相應的改變,由於兩個主板的實現示意差不多,因此還是隻示例一個,示例代碼如下
java代碼:
- /**
- * 技嘉主板類,命令的真正實現者,在Command模式中充當Receiver
- */
- public class GigaMainBoard implements MainBoardApi{
- /**
- * 真正的開機命令的實現
- */
- public void open(){
- System.out.println("技嘉主板現在正在開機,請等候");
- System.out.println("接通電源......");
- System.out.println("設備檢查......");
- System.out.println("裝載系統......");
- System.out.println("機器正常運轉起來......");
- System.out.println("機器已經正常打開,請操作");
- }
- /**
- * 真正的重新啓動機器命令的實現
- */
- public void reset(){
- System.out.println("技嘉主板現在正在重新啓動機器,請等候");
- System.out.println("機器已經正常打開,請操作");
- }
- }
(2)該來定義命令和按鈕了,命令接口沒有任何變化,原有的開機命令的實現也沒有任何變化,只是新添加了一個重啓命令的實現,示例代碼如下:
java代碼:
- /**
- * 重啓機器命令的實現,實現Command接口,
- * 持有重啓機器命令的真正實現,通過調用接收者的方法來實現命令
- */
- public class ResetCommand implements Command{
- /**
- * 持有真正實現命令的接收者——主板對象
- */
- private MainBoardApi mainBoard = null;
- /**
- * 構造方法,傳入主板對象
- * @param mainBoard 主板對象
- */
- public ResetCommand(MainBoardApi mainBoard) {
- this.mainBoard = mainBoard;
- }
- public void execute() {
- //對於命令對象,根本不知道如何重啓機器,會轉調主板對象
- //讓主板去完成重啓機器的功能
- this.mainBoard.reset();
- }
- }
(3)持有命令的機箱也需要修改,現在不只一個命令按鈕了,有兩個了,所以需要在機箱類裏面新添加重啓的按鈕,爲了簡單,沒有做成集合。示例代碼如下:
java代碼:
- /**
- * 機箱對象,本身有按鈕,持有按鈕對應的命令對象
- */
- public class Box {
- private Command openCommand;
- public void setOpenCommand(Command command){
- this.openCommand = command;
- }
- public void openButtonPressed(){
- //按下按鈕,執行命令
- openCommand.execute();
- }
- /**
- * 重啓機器命令對象
- */
- private Command resetCommand;
- /**
- * 設置重啓機器命令對象
- * @param command
- */
- public void setResetCommand(Command command){
- this.resetCommand = command;
- }
- /**
- * 提供給客戶使用,接收並相應用戶請求,相當於重啓按鈕被按下觸發的方法
- */
- public void resetButtonPressed(){
- //按下按鈕,執行命令
- resetCommand.execute();
- }
- }
(4)看看客戶如何使用這兩個按鈕,示例代碼如下
java代碼:
- public class Client {
- public static void main(String[] args) {
- //1:把命令和真正的實現組合起來,相當於在組裝機器,
- //把機箱上按鈕的連接線插接到主板上。
- MainBoardApi mainBoard = new GigaMainBoard();
- //創建開機命令
- OpenCommand openCommand = new OpenCommand(mainBoard);
- //創建重啓機器的命令
- ResetCommand resetCommand = new ResetCommand(mainBoard);
- //2:爲機箱上的按鈕設置對應的命令,讓按鈕知道該幹什麼
- Box box = new Box();
- //先正確配置,就是開機按鈕對開機命令,重啓按鈕對重啓命令
- box.setOpenCommand(openCommand);
- box.setResetCommand(resetCommand);
- //3:然後模擬按下機箱上的按鈕
- System.out.println("正確配置下------------------------->");
- System.out.println(">>>按下開機按鈕:>>>");
- box.openButtonPressed();
- System.out.println(">>>按下重啓按鈕:>>>");
- box.resetButtonPressed();
- //然後來錯誤配置一回,反正是進行參數化配置
- //就是開機按鈕對重啓命令,重啓按鈕對開機命令
- box.setOpenCommand(resetCommand);
- box.setResetCommand(openCommand);
- //4:然後還是來模擬按下機箱上的按鈕
- System.out.println("錯誤配置下------------------------->");
- System.out.println(">>>按下開機按鈕:>>>");
- box.openButtonPressed();
- System.out.println(">>>按下重啓按鈕:>>>");
- box.resetButtonPressed();
- }
- }
運行一下看看,很有意思,結果如下: