命令模式是行爲型設計模式的一種,其目的是將一個請求封裝爲一個對象,從而使你能夠用不一樣的請求對客戶進行參數化。與另外一種將每種命令與調用命令的對象結合造成一個專有類的方式相比,命令模式的優勢有將調用操做的對象與知道如何實現該操做的對象解耦,增長新的命令不須要修改現有的類。
命令模式的結構以下:
參與者有:
1.Invoker請求者
要求該命令執行這個請求,即命令的調用者
2.Command接口
3.ConcreteCommand具體接口
4.Receiver接收者
命令的相關操做的實際實施者
5.Client
協做過程:
1.Client建立一個ConcreteCommand對象並指定它的Receiver對象
2.某Invoker對象存儲該ConcreteCommand對象
3.該Invoker經過調用Command對象的Excute操做來提交一個請求。若該命令是可撤消的,ConcreteCommand就在執行Excute操做以前存儲當前狀態以用於取消該命令
4.ConcreteCommand對象對調用它的Receiver的一些操做以執行該請求redis
// commands.go // Invoker請求者接口 type Cmdable interface { Pipeline() Pipeliner Pipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error) TxPipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error) TxPipeline() Pipeliner Command(ctx context.Context) *CommandsInfoCmd ClientGetName(ctx context.Context) *StringCmd // ... // 和全部Redis命令的相關方法 } // cmdable實現了Cmdable接口 type cmdable func(ctx context.Context, cmd Cmder) error func (c cmdable) Echo(ctx context.Context, message interface{}) *StringCmd { cmd := NewStringCmd(ctx, "echo", message) _ = c(ctx, cmd) return cmd }
這裏值得一提的是cmdable是一個函數類型,func(ctx context.Context, cmd Cmder) error
而且每一個cmdable方法裏都會有_ = c(ctx, cmd),也就是如何去調用cmd在這裏尚未明確寫出
再回頭看redis.go,會發現這樣一段代碼設計模式
type Client struct { *baseClient cmdable hooks ctx context.Context } func NewClient(opt *Options) *Client { opt.init() c := Client{ baseClient: newBaseClient(opt, newConnPool(opt)), ctx: context.Background(), } c.cmdable = c.Process //劃線 return &c }
c.cmdable = c.Process這行指定了請求如何調用Command的
在ctrl+左鍵追蹤幾層後,會在redis.go裏找到調用的具體過程函數
// redis.go func (c *baseClient) process(ctx context.Context, cmd Cmder) error { ...... err := cn.WithWriter(ctx, c.opt.WriteTimeout, func(wr *proto.Writer) error { eturn writeCmd(wr, cmd) }) err = cn.WithReader(ctx, c.cmdTimeout(cmd), cmd.readReply) ......
而後再去找Command,這邊就比較清晰了,都在command.go中工具
// command.go // Command接口 type Cmder interface { Name() string FullName() string Args() []interface{} String() string stringArg(int) string firstKeyPos() int8 setFirstKeyPos(int8) readTimeout() *time.Duration readReply(rd *proto.Reader) error SetErr(error) Err() error } // 還有許多Cmder的具體實現,其中一個實現的部分代碼以下 type XPendingExtCmd struct { baseCmd val []XPendingExt } func (cmd *XPendingExtCmd) Val() []XPendingExt { return cmd.val }
在這裏沒有看到Receiver,是由於每一個Cmder實現都本身實現了全部功能,根本不須要額外的接收者對象。設計
有時必須向某對象提交請求,但並不知道關於被請求的操做或請求的接受者的任何信息。這個時候能夠用到命令模式,經過將請求自己變成一個對象來使工具箱對象可向未指定的應用對象提出請求。code