命令模式是對象行為型使用率比較高的設計模式,別名:Action(動作),Transaction(事務)
意圖: 將一個請求封裝為一個對象,從而使你可對不同的請求進行參數化;對請求排隊或記錄請求日誌,以及支援可取消的操作
這裡所謂的“不同的請求”也既意味著請求可能發生的變化,是一個可能擴充的功能點。
動機: 方便擴充
結構:
協作說明:
參與角色:
Command 聲明一個介面以用來實現某個操作。
ConcreteCommand 將動作與Reciver對外綁定,通過調用Reciver對象的相應方法來實現Command的方法。
Client 建立ConcreteCommand對象,並設定其Reciver對象。
Invoker 要求該Command實現請求。
Reciver 知道如何?具體的請求的類。
用戶端建立了一個具體的Command對象並指定了其接收者。
調用者Object Storage Service了此具體的Command對象。
調用者對象通過執行Command對象的Execute方法來實現當前請求。
如果命令是可以撤銷時,具體對象在調用執行方法前將儲存相應的狀態以用來命令此請求。
具體的Command對象調用其接收者的方法從而來實現相應請求。
適用性:
類似於 MenuItem , 抽象出待執行的動作以參數化某對象
在不同的時刻指定,排列,執行請求
支援撤消
支援修改日誌
在構建在原語操作上的高層操作構造一個系統(其實就是事務)
動態性方面: 像ruby中 block 就是命令模式
效果:
命令模式將調用者對象與接收對象解耦(調用與實現解耦)。調用者實現功能時只需調用Command介面的Execute方法。
具體的Commands對象是第一層對象,它們可以像其他對象一樣被擴充或操作。
你可以將多個Commands對象彙總成一個組合命令。組合命令也是組合對象模式的一個執行個體,將命令排隊也是其的一種特殊情況。
你可以很容易的添加新的命令,因為你並不需要修改現有的代碼。這也符合了開閉原則,對修改關閉,對擴充開放。
實現時應考慮命令對象應達到何種智能程式和支援撤消和重做這兩個問題.
誤用:
不要著迷 到底哪個簡單?
命令模式不是說“做這個” 說“ 記住這個如何做”,稍後再說”按照我剛才要你記住的方法做這個”
小心撤銷,許多操作是破壞性的,如刪除檔案操作
類圖:
class Button attr_accessor :name, :command def initialize name, command @name = name @command = command end def do_something @command.execute end endclass Command def execute "root execute" end endclass PaintCommand < Command def execute "draw something" end endclass VocalCommand < Command def execute "talk something" end endpaintCommand = PaintCommand.newvocalCommand = VocalCommand.newbutton = Button.new("button", paintCommand)p button.do_somethingbutton.command = vocalCommandp button.do_something
定義了主體類Button,Button彙總一個命令對象Command,聲明Command,PaintCommand,VocalCommand三個具有繼承的命令類,在系統當中可能存在有多個Button,每個Button所要完成的事情是不一樣的,即這個部分是變化的的,也就是方法do_something中的代碼也是不確定的,將這部分的代碼分離到單獨的對象中進行管理,而這個對象就被稱為命令對象,命令對象只負責需要完成的任務或者是指令,主體對象可以根據自己的需要在任何時間去調用需要的命令進行執行。在調用處的代碼中也非常清晰的發現要切換當前Button的命令實現非常方便,也非常靈活,只需要簡單的卻調用set方法就可以完成。如果採用Button繼承的關係,第一主體對象會造成類爆炸,第二在切換命令實現的時候對比這種方式就會比較困難。
使用ruby proc來完成命令模式 :
class Button attr_accessor :name def initialize name, &command @name = name end def do_something &command command.call end endpaint_command = lambda do p "paint something"endvocal_command = lambda do p "talk something"endbutton = Button.new ("name")button.do_something &vocal_commandbutton.do_something &paint_command
可以看到使用block來代替命令類更加簡單,易懂,在實際項目環境中使用proc和命令可以情況而定,如果命令對象非常複雜,需要有自己的狀態和方法,就選用命令類來完成,如果只是簡單的處理一些小事情,便可以採用proc
如果需要執行的命令過多,可以定義命令隊列,也就是一個命令裡面管理多個命令, 當調用的時候挨個調用每個命令進行執行,從這一點來非常像組合模式
在某中意義上來說觀察者模式和命令模式有一些相像,都是彙總一些具有共同特徵的對象到自己類,然後根據情況來進行調用。但是2個模式有一個明顯的區別,就是用途。觀察者模式用於被觀察者將變化通知到各個不同的觀察者身上,而命令模式並不關心是否是通知到其他命令,命令對象只負責執行自己的任務或者是指令,並且命令模式可以記住前一次的操作,所以一般來說很多文字編輯器的撤銷/重做都會用到命令模式。