設計模式之命令模式(三)

  • 2019 年 12 月 25 日
  • 筆記

我回來啦!今天是周六,一看命令模式還有一個總結未完成,趕緊爬起來做做好。

就如上一篇所說的,如果擁有了一個遙控器,卻無法光憑按下一個按你,就同時能弄暗燈光、打開音響和電視、設置到DVD,並讓熱水器開始加溫,那麼我要這個遙控器還有什麼意義呢?

使用宏命令

根據比較高級的想法來看,就是我們需要製造一種新的命令,用來執行其他一堆命令,而不只是執行一個命令,這樣就是一個不錯的想法了吧。這就是我們將要說的宏命令。

public class MacroCommand implements Command {      // 在宏命令中,用命令數組存儲一大堆命令      Command[] commands;        public MacroCommand(Command[] commands) {          this.commands = commands;      }        public void execute() {          for (int i = 0; i < commands.length; i++) {          // 當這個宏命令被遙控器執行時,就一次性執行數組裡的每個命令              commands[i].execute();          }      }        /**       * NOTE:  these commands have to be done backwards to ensure       * proper undo functionality       */      public void undo() {          for (int i = commands.length -1; i >= 0; i--) {              commands[i].undo();          }      }  }  

讓我們來看下如何使用宏命令:

  1. 先創建想要進入宏的命令集合
Light light = new Light("Living Room");  TV tv = new TV("Living Room");  Stereo stereo = new Stereo("Living Room");  Hottub hottub = new Hottub();    LightOnCommand lightOn = new LightOnCommand(light);  StereoOnCommand stereoOn = new StereoOnCommand(stereo);  TVOnCommand tvOn = new TVOnCommand(tv);  HottubOnCommand hottubOn = new HottubOnCommand(hottub);  
  1. 接下來創建兩個數組,其中一個用來記錄開啟命令,另一個用來記錄關閉命令,並在數組內放入對應的命令
Command[] partyOn = { lightOn, stereoOn, tvOn, hottubOn};  Command[] partyOff = { lightOff, stereoOff, tvOff, hottubOff};    MacroCommand partyOnMacro = new MacroCommand(partyOn);  MacroCommand partyOffMacro = new MacroCommand(partyOff);  
  1. 然後將宏命令指定給我們所希望的按鈕:
remoteControl.setCommand(0, partyOnMacro, partyOffMacro);  
  1. 最後,只需按下一些按鈕,測試是否正常工作
System.out.println(remoteControl);  System.out.println("--- Pushing Macro On---");  remoteControl.onButtonWasPushed(0);  System.out.println("--- Pushing Macro Off---");  remoteControl.offButtonWasPushed(0);  

不會忘記我們的撤銷功能

public void undo() {      for (int i = commands.length -1; i >= 0; i--) {          commands[i].undo();      }  }

命令模式的更多用途:隊列請求

命令可以將運算塊打包(一個接收者和一組動作),然後將它傳來傳去,就像是一般的對象一樣。現在,即使在命令被創建許久之後,運算依然可以被調用。事實上,它甚至可以在不同的執行緒中被調用。我們可以利用這樣的特性衍生一些應用,例如:日程安排、執行緒池、工作隊列等。

想像一個工作隊列:你再某一端添加命令,然後另一端則是執行緒。執行緒進行下面的動作:從隊列中取出一個命令,調用它的execute()方法,等待這個調用完成,然後將此命令對象丟棄,再取出下一個命令。。。

請注意,工作隊列類和進行計算的對象之間完全是解耦的。此刻執行緒可能在進行財務運算,下一刻卻在讀取網路數據。工作隊列對象不在乎到底做些什麼,他們只知道取出命令對象,然後調用其execute()方法。

命令模式的更多用途:日誌請求

某些應用需要我們將所有的動作都記錄在日誌中,並能在系統死機之後,重新調用這些動作恢復到之前的狀態。命令模式能夠支援這一點。

當我們執行命令的時候,將歷史記錄存儲在磁碟中,一旦系統死機,我們就可以將命令對象重新載入,並成批地依次調用這些對象的execute()方法。

比如有許多調用大型數據結構的應用無法在每次改變發生時被快速地存儲。通過使用記錄日誌,我們可以將上次檢查點之後的所有操作記錄下來,如果系統出狀況,從檢查點開始應用這些操作。比如說,對於電子表格應用,我們可能想要實現的錯誤回復方式是將電子表格的操作記錄在日誌中,而不是每次電子表格一有變化就記錄整個電子表格。

對於更高級的應用而言,這些技巧可以被擴展應用到事務處理中,也就是說,一整群操作必須全部進行完成,或者沒有進行任何的操作

設計箱內的工具

還是按照之前的套路,總結下工具箱內新增的工具吧

  • OO基礎 抽象、封裝、繼承、多態
  • OO原則 封裝變化 多用組合,少用繼承 針對介面編程,不針對實現編程 為交互對象之間的松耦合設計而努力 依賴抽象,不要依賴具體類 類應該對擴展開放,對修改關閉
  • OO模式 『策略模式』、『觀察者模式』、『裝飾者模式』、『抽象工廠模式』、『工廠方法模式』、『單例模式』 『命令模式』將請求封裝成對象,這可以讓你使用不同的請求、隊列,或者日誌請求來參數化其他對象。命令模式也可以支援撤銷操作。

我們學習命令模式,就是一個循序漸進的過程,先從簡單的開關開始,然後再到複雜的開啟狀態,進而想到隊列,想到記錄日誌這種事務操作。學習也是一個循序漸進的過程,我們通過簡單的Demo學習,然後到項目實踐,再到後面獨立設計框架,我相信大家都能悟出這個道理來。

持續學習,精進學習,我們會一起加油。下一次,我們將學習適配器模式與外觀模式