命令模式是一種行為型模式。它建議將請求封裝為一個獨立的物件。在這個物件裡包含請求相關的全部資訊,因此可以將其獨立執行。
在命令模式中有如下基礎元件:
- 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!