Design Pattern(11) - Command
July 06, 2017
以下文章是閱讀
深入淺出Design Pattern
還有
聖經還有Source making的筆記
圖片截圖自lynda.com的Foundations of Programming: Design Patterns 要更深入的理解一定要去看這兩本書
這篇文章我還用了官方的github code來說明
電視遙控器
把電視想像成一個隨時在接收request的server 今天我們手上有一個很陽春的遙控器 也就是invoker
當我們想把音量調大 就compose一個把音量調大的request讓遙控器client發出request給電視server 要往上轉的時候也是compose一個頻道+1的request給server
這樣實在太麻煩了 很多命令/request我們根本就會一用再用 而且命令本身跟接受方都是不會變的 這種時候比較好的做法是把命令本身實體化 然後這個命令object會被遙控器的特定按鈕trigger 這也是我們今日的遙控器
意思就是 我把 把電視頻道+1 這個命令實體化 可以被遙控器上的 +1 觸發
陽春版
先來實作一個陽春的 這個遙控器一次只能裝載一個命令 只有一個按鈕
每次當你要發不同的命令 就要更新這個遙控器的command variable
Command是我們的interface 這個interface長得很簡單
所以我們的遙控器裡面有一個variable是command 你可以setCommand之後 按button去execute這個command
因為Command interface有execute這個函式 所以slot可以安心call execute()
電視提供開跟關的功能
那麼client怎麼call呢
挺好懂的 執行結果
code看起來很簡單 但其中有一個奧秘我想特別講一下
上面的code是design pattern的官方github裡的code 馬上可以直接執行
但執行完後 怎麼看都不對勁 為什麼compile會過???
setCommand 吃一個Command 可是tv::on是method reference 我的code從來就沒有說這個method實作了Command
為什麼可以這樣丟進去呢??
有興趣的讀者可以想一想 java初學者如我花了20min 我以後會另外寫一篇functional interface的文章來解釋這個問題
開電視是一個class
開電視這個command變成一個class 我們把遙控器的command設定成這個class的物件
Client比剛剛好懂 我需要new一個command的object 丟進setCommand
簡單
命令模式
把一個request封裝成一個物件 因為每個物件可以有不同的參數 所以可以客製化對於客戶的請求
結構
-
Command: 命令的interface 所有具體物件需要實作這個interface
-
ConcreteCommand(開電視命令): 將Receiver和相對應的動作連結起來 實作execute
-
Client(你): 建立ConcreteCommand 設定receiver
-
Invoker(遙控器): 要求Command執行命令
-
Receiver(TV): 依據命令執行相對應動作
優缺點
1.請求方和接受方獨立 降低依賴 有新的命令也很容易新增
2.請求本身是一個object 就可以serialize或deserialize去存儲或傳遞
3.可以輕易的支援undo和redo
4.Invoker針對Command介面寫程式 不需要知道ConcreteCommand有幾個或是怎麼實作
5.實作Macro Command容易 可以run time組合任意commands
6.但可能導致過多的命令class 因為所有的command都需要一個class
正常的遙控器
一般的遙控器 每個按鈕都是不同的command request 我們現在來簡單的實作他
遙控器裡面原本只有一個command 現在需要變成command array
我們還實做undo 需要一個變數來記得上一個command是什麼
要實作undo 記得修改Command interface
NoCommand是一個空物件null object 可以當一個interface的default value
Client的用法也很簡單
1.Create receiver
2.Create ConcreteCommand
3.Set command for each button
每一個command都是個物件
當然別忘了在實作ConcreteCommand的時候 需要實作undo
一鍵懶人包
前菜上完 有趣的來了
把Command具體化的好處還有一個 我們可以把很多個command包成一個MacroCommand
注意這裡undo的順序是反過來的
Client就可以自創MacroCommand
太精彩了 A list of command is still a command!
這樣就可以動態創造你想要的任何combination of commands
總結
命令模式還有一些其他比較常見的用途
1.因為command本身是物件 就很容易用一個command queue存起來 讓receiver慢慢處理
2.還可以把命令序列化 當成log存起來 server掛了的話還可以從disk去redo所有的命令