命令模式是行为型设计模式的一种,其目的是将一个请求封装为一个对象,从而使你能够用不一样的请求对客户进行参数化。与另外一种将每种命令与调用命令的对象结合造成一个专有类的方式相比,命令模式的优势有将调用操做的对象与知道如何实现该操做的对象解耦,增长新的命令不须要修改现有的类。
命令模式的结构以下:
参与者有:
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