命令模式與go-redis command設計

1、什麼是命令(Command)模式

命令模式是行爲型設計模式的一種,其目的是將一個請求封裝爲一個對象,從而使你能夠用不一樣的請求對客戶進行參數化。與另外一種將每種命令與調用命令的對象結合造成一個專有類的方式相比,命令模式的優勢有將調用操做的對象與知道如何實現該操做的對象解耦,增長新的命令不須要修改現有的類。
命令模式的結構以下:
command
參與者有:
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

2、go-redis command相關代碼

// 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實現都本身實現了全部功能,根本不須要額外的接收者對象。設計

3、總結

有時必須向某對象提交請求,但並不知道關於被請求的操做或請求的接受者的任何信息。這個時候能夠用到命令模式,經過將請求自己變成一個對象來使工具箱對象可向未指定的應用對象提出請求。code

相關文章
相關標籤/搜索