命令模式是一種行為型模式。它建議將請求封裝為一個獨立的對象。在這個對象里包含請求相關的全部信息,因此可以將其獨立執行。
在命令模式中有如下基礎組件:
- Receiver:唯一包含業務邏輯的類,命令對象會將請求傳遞給它,請求的最終處理者
- Command:組裝了一個
Receiver
成員,並綁定實現了Receiver
的一個特定行為 - Invoker:請求的發送者,組裝了
Command
成員,通過調用Command
實例的execute()
方法來觸發對應的指令 - Client:通過將
Receiver
實例傳遞給Command
構造器來創建Command
對象,之后會將創建的對象同Invoker
綁定。
還是通過一個具體的場景來理解下命令模式是怎樣運行的。以打開電視這個行為舉例,我們可以通過如下方式打開電視:
- 通過遙控器開關打開電視
- 通過電視上的開關打開電視
在這個場景中,我們有一個指令(Command)是“打開電視”,指令的接收者(Receiver)當然就是電視(TV)了,當我們執行(execute)指令時,相關指令就會讓電視打開(TV.on())。
再明確下這個場景中的所有組件:
Receiver
是TV
Command
只有一個,是打開電視:ON
,這個指令需要組裝TV
成員Invoker
是遙控打開或開關打開這兩種方式,它們會組裝ON
指令成員。
注意,這里我們將“打開電視”這個請求封裝到了一個ON
指令對象中,這個指令可以被不同的調用方調用。在ON
指令中嵌入了TV
實例,可以被獨立執行。
再舉個例子,想想PhotoShop這個軟件,在PhotoShop中,要執行“保存”操作有三種方式:
- 從右鍵菜單中執行保存
- 從工具欄菜單中執行保存
- 使用Ctrl+S快捷鍵
這三種操作做的是同一件事情:保存正在編輯的圖片。這三種操作的保存行為可以抽象為一個“Save”指令對象,而正在被編輯的圖片就可以視為一個Receiver
。
現在總結下使用命令對象的好處:
- 抽象出了潛藏的真實業務邏輯,並將其和具體的操作解耦
- 不需要為每個調用者創建不同的處理器
- 指令對象包含了執行所需的全部信息,因此它也適用於需要延遲執行的場景
看下UML類圖:
- 注意下
Invoker
是怎樣嵌入指令對象的。當一個請求發送給Invoker
的時候,Invoker
會將這個請求傳遞給其嵌入的命令對象。 - 所有具體的指令類都會組裝一個
Receiver
成員屬性。
代碼如下:
代碼如下:
command.go(指令 interface)
type command interface { execute() }
device.go(Receiver interface)
type device interface { on() off() }
tv.go(Receiver)
import "fmt" type tv struct { isRunning bool } func (t *tv) on() { t.isRunning = true fmt.Println("Turning tv on") } func (t *tv) off() { t.isRunning = false fmt.Println("Turning tv off") }
onCommand.go(指令)
type onCommand struct { device device } func (c *onCommand) execute() { c.device.on() }
offCommand.go(指令)
type offCommand struct { device device } func (c *offCommand) execute() { c.device.off() }
button.go(Invoker,開關打開電視)
type button struct { command command } func (b *button) press() { b.command.execute() }
main.go(Client)
func main() { tv := &tv{} onCommand := &onCommand{ device: tv, } offCommand := &offCommand{ device: tv, } onButton := &button{ command: onCommand, } onButton.press() offButton := &button{ command: offCommand, } offButton.press() }
運行結果:
Turning tv on Turning tv off
代碼已上傳至GitHub:github / zhyea / command
END!