GoLang設計模式08 - 命令模式

robin·張發表於2021-10-02

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

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

  • 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!

 

相關文章