Go設計模式(26)-命令模式

命令模式很多同學可能不會用到,但這個模式還是蠻有意思的。命令模式能夠將操作和資料打包成物件,便於系統對命令進行管理、維護。

UML類圖位置:https://www。processon。com/view/link/60d29bf3e401fd49502afd25

本文程式碼連結為:https://github。com/shidawuhen/asap/blob/master/controller/design/26command。go

1。定義

1。1命令模式

命令模式

:將一個請求封裝為一個物件,從而使你可用不同的請求對客戶進行引數化;對請求排隊或記錄請求日誌,以及支援可撤銷的操作。

UML

Go設計模式(26)-命令模式

1。2分析

上面的定義和UML比較複雜,大家可能比較難理解。

首先我們需要明白什麼是命令。命令包括指令和資料。指令是行為,資料影響到指令。如前進3米,前進是指令,3米是資料。

然後我們再看一下各個類的含義。

Command和ConcreteCommand是命令,有Excute函式,代表要做的行為。

ConcreteCommand呼叫Excute(),最終呼叫Receiver的Action。這意味ConcreteCommand只是一個容器,真正的操作邏輯在Receiver中。

Invoker包含了所有Command,控制Command何時執行Excute()。

現在我們將UML簡化,把Invoker、Receiver去掉,看看是否容易理解了。

Go設計模式(26)-命令模式

透過這個簡潔版UML,我們來看一下為什麼要用命令模式。

命令包括指令和資料,指令其實對應著操作,操作在程式碼中對應著函式。

命令模式其實是把函式封裝成物件,系統能對物件進行各種操作,如排隊執行、記錄日誌、撤銷等。

為什麼要將函式包裝成物件呢?C、C++、Go支援函式指標,但並不是所有語言都有這種特性,這時命令模式就起作用了。而且即使語言支援函式指標,命令的資料部分怎麼存放仍是一個問題。

所以簡單理解,命令模式就是把請求打包成一個一個Command物件,儲存起來,系統根據實際需求進行處理。

2。應用場景

大家可能感覺命令模式與MQ、工廠模式一樣,其實在細節上是有區別的:

MQ只包含資料,不包含行為,命令模式兩者都包含

工廠模式需要實時執行,但命令模式可以進行儲存,延後執行

命令模式我從來沒有用過。《設計模式之美》裡講了遊戲研發的一種通用架構:客戶端的請求被服務端儲存起來,服務端有單獨執行緒處理這些請求。那我們就按這個場景寫一下程式碼實現。

3。程式碼實現

package mainimport “fmt”/** * @Author: Jason Pang * @Description: 命令介面 */type Command interface {   Execute()}/** * @Author: Jason Pang * @Description: 移動命令 */type MoveCommand struct {   x, y int64}/** * @Author: Jason Pang * @Description: 如何移動 * @receiver m */func (m *MoveCommand) Execute() {   fmt。Printf(“向右移動%d,向上移動%d \n”, m。x, m。y)}/** * @Author: Jason Pang * @Description: 攻擊命令 */type AttackCommand struct {   skill string}/** * @Author: Jason Pang * @Description: 如何攻擊 * @receiver a */func (a *AttackCommand) Execute() {   fmt。Printf(“使用技能%s\n”, a。skill)}/** * @Author: Jason Pang * @Description: 記錄命令 * @param action * @return Command */func AddCommand(action string) Command {   if action == “attack” {      return &AttackCommand{         skill: “野蠻衝撞”,     }   } else { //預設是移動      return &MoveCommand{         x: 10,         y: 20,     }   }}func main() {   //將命令記錄   lc := make([]Command, 0)   lc = append(lc, AddCommand(“attack”))   lc = append(lc, AddCommand(“move”))   lc = append(lc, AddCommand(“move”))   lc = append(lc, AddCommand(“attack”))   //執行命令   for _, c := range lc {      c。Execute()   }}

輸出:

➜ myproject go run main。go

使用技能野蠻衝撞

向右移動10,向上移動20

向右移動10,向上移動20

使用技能野蠻衝撞

透過上面的程式碼,大家應該能夠理解命令模式了。可以看出,對不同請求,生成不同的Command,Command中包含對應的資料與操作。這也是模式定義中說到的”對請求排隊或記錄請求日誌,以及支援可撤銷的操作“。

總結

設計模式是為了解決現實中的問題,我們需要和具體場景相繫結。在解決問題的時候,採用的是不是標準的設計模式並不重要,模式只是手段,手段需要為達成目的服務。

最後

大家如果喜歡我的文章,可以關注我的公眾號(程式設計師麻辣燙)

我的個人部落格為:https://shidawuhen。github。io/

往期文章回顧:

設計模式

招聘

思考

儲存

算法系列

讀書筆記

小工具

架構

網路

Go語言