命令模式很多同學可能不會用到,但這個模式還是蠻有意思的。命令模式能夠將操作和資料打包成物件,便於系統對命令進行管理、維護。
UML類圖位置:https://www。processon。com/view/link/60d29bf3e401fd49502afd25
本文程式碼連結為:https://github。com/shidawuhen/asap/blob/master/controller/design/26command。go
1。定義
1。1命令模式
命令模式
:將一個請求封裝為一個物件,從而使你可用不同的請求對客戶進行引數化;對請求排隊或記錄請求日誌,以及支援可撤銷的操作。
UML
:
1。2分析
上面的定義和UML比較複雜,大家可能比較難理解。
首先我們需要明白什麼是命令。命令包括指令和資料。指令是行為,資料影響到指令。如前進3米,前進是指令,3米是資料。
然後我們再看一下各個類的含義。
Command和ConcreteCommand是命令,有Excute函式,代表要做的行為。
ConcreteCommand呼叫Excute(),最終呼叫Receiver的Action。這意味ConcreteCommand只是一個容器,真正的操作邏輯在Receiver中。
Invoker包含了所有Command,控制Command何時執行Excute()。
現在我們將UML簡化,把Invoker、Receiver去掉,看看是否容易理解了。
透過這個簡潔版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語言