GoLang設計模式08 – 命令模式

命令模式是一種行為型模式。它建議將請求封裝為一個獨立的對象。在這個對象里包含請求相關的全部資訊,因此可以將其獨立執行。

在命令模式中有如下基礎組件:

  • Receiver:唯一包含業務邏輯的類,命令對象會將請求傳遞給它,請求的最終處理者
  • Command:組裝了一個Receiver成員,並綁定實現了Receiver的一個特定行為
  • Invoker:請求的發送者,組裝了Command成員,通過調用Command實例的execute()方法來觸發對應的指令
  • Client:通過將Receiver實例傳遞給Command構造器來創建Command對象,之後會將創建的對象同Invoker綁定。

還是通過一個具體的場景來理解下命令模式是怎樣運行的。以打開電視這個行為舉例,我們可以通過如下方式打開電視:

  1. 通過遙控器開關打開電視
  2. 通過電視上的開關打開電視

在這個場景中,我們有一個指令(Command)是「打開電視」,指令的接收者(Receiver)當然就是電視(TV)了,當我們執行(execute)指令時,相關指令就會讓電視打開(TV.on())。

再明確下這個場景中的所有組件:

  • ReceiverTV
  • Command只有一個,是打開電視:ON,這個指令需要組裝TV成員
  • Invoker是遙控打開或開關打開這兩種方式,它們會組裝ON指令成員。

注意,這裡我們將「打開電視」這個請求封裝到了一個ON指令對象中,這個指令可以被不同的調用方調用。在ON指令中嵌入了TV實例,可以被獨立執行。

再舉個例子,想想PhotoShop這個軟體,在PhotoShop中,要執行「保存」操作有三種方式:

  1. 從右鍵菜單中執行保存
  2. 從工具欄菜單中執行保存
  3. 使用Ctrl+S快捷鍵

這三種操作做的是同一件事情:保存正在編輯的圖片。這三種操作的保存行為可以抽象為一個「Save」指令對象,而正在被編輯的圖片就可以視為一個Receiver

現在總結下使用命令對象的好處:

  1. 抽象出了潛藏的真實業務邏輯,並將其和具體的操作解耦
  2. 不需要為每個調用者創建不同的處理器
  3. 指令對象包含了執行所需的全部資訊,因此它也適用於需要延遲執行的場景

看下UML類圖:

  1. 注意下Invoker是怎樣嵌入指令對象的。當一個請求發送給Invoker的時候,Invoker會將這個請求傳遞給其嵌入的命令對象。
  2. 所有具體的指令類都會組裝一個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!