Command-命令¶
备注
creates objects that encapsulate actions and parameters.
备注
The command pattern encapsulates a request as an object, thereby letting us parameterize other objects with different requests, queue or log requests, and support undoable operations.命令模式将请求(命令)封装为一个对象,这样可以使用不同的请求参数化其他对象(将不同请求依赖注入到其他对象),并且能够支持请求(命令)的排队执行、记录日志、撤销等(附加控制)功能。
实现:
1. 把函数封装成对象,实现相同接口,使其可以通过参数传递
2. [函数式语言专属] 把函数当参数传递
后面会有两个例子详细说明
应用场景:
用来控制命令的执行,如:
异步、延迟、排队执行的命令、撤销重做命令、
存储命令、给命令记录日志等
命令模式 VS 策略模式¶
每个设计模式都应该由两部分组成:
第一部分是应用场景,即这个模式可以解决哪类问题;
第二部分是解决方案,即这个模式的设计思路和具体的代码实现。
设计模式之间的主要区别还是在于设计意图,也就是应用场景。
单纯地看设计思路或者代码实现,有些模式确实很相似,比如策略模式和工厂模式。
命令模式 VS 策略模式的区别:
在策略模式中,不同的策略具有相同的目的、不同的实现、互相之间可以替换。
比如,BubbleSort、SelectionSort 都是为了实现排序的,
只不过一个是用冒泡排序算法来实现的,另一个是用选择排序算法来实现的。
而在命令模式中,不同的命令具有不同的目的,对应不同的处理逻辑,并且互相之间不可替换。
命令模式: 通过不同的事件执行不同的命令
策略模式: 通过不同的配置或参数使用不同的算法
代码实现¶
实例1-将函数封装为对象¶
实例:
// 定义接口: 后面封装成对象的命令会实现此接口
type ICommand interface {
Execute() error
}
// 实现1: StartCommand 游戏开始运行
type StartCommand struct{}
// NewStartCommand NewStartCommand
func NewStartCommand( /*正常情况下这里会有一些参数*/ ) *StartCommand {
return &StartCommand{}
}
// Execute Execute
func (c *StartCommand) Execute() error {
fmt.Println("game start")
return nil
}
// 实现2: ArchiveCommand 游戏存档
type ArchiveCommand struct{}
// NewArchiveCommand NewArchiveCommand
func NewArchiveCommand( /*正常情况下这里会有一些参数*/ ) *ArchiveCommand {
return &ArchiveCommand{}
}
// Execute Execute
func (c *ArchiveCommand) Execute() error {
fmt.Println("game archive")
return nil
}
使用:
func TestDemo(t *testing.T) {
// 用于测试,模拟来自客户端的事件
eventChan := make(chan string)
go func() {
events := []string{"start", "archive", "start", "archive", "start", "start"}
for _, e := range events {
eventChan <- e
}
}()
defer close(eventChan)
// 使用命令队列缓存命令
commands := make(chan ICommand, 1000)
defer close(commands)
go func() {
for {
// 从请求或者其他地方获取相关事件参数
event, ok := <-eventChan
if !ok {
return
}
var command ICommand
switch event {
case "start":
command = NewStartCommand()
case "archive":
command = NewArchiveCommand()
}
// 将命令入队
commands <- command
}
}()
for {
select {
case c := <-commands:
c.Execute()
case <-time.After(1 * time.Second):
fmt.Println("timeout 1s")
return
}
}
}
实例2-将函数直接作为参数¶
实例:
// 把函数当对象/参数
type Command func() error
// StartCommandFunc 返回一个 Command 命令
// 是因为正常情况下不会是这么简单的函数
// 一般都会有一些参数
func StartCommandFunc() Command {
return func() error {
fmt.Println("game start")
return nil
}
}
// ArchiveCommandFunc ArchiveCommandFunc
func ArchiveCommandFunc() Command {
return func() error {
fmt.Println("game archive")
return nil
}
}
使用:
func TestDemoFunc(t *testing.T) {
// 用于测试,模拟来自客户端的事件
eventChan := make(chan string)
go func() {
events := []string{"start", "archive", "start", "archive", "start", "start"}
for _, e := range events {
eventChan <- e
}
}()
defer close(eventChan)
// 使用命令队列缓存命令
commands := make(chan Command, 1000)
defer close(commands)
go func() {
for {
// 从请求或者其他地方获取相关事件参数
event, ok := <-eventChan
if !ok {
return
}
var command Command
switch event {
case "start":
command = StartCommandFunc()
case "archive":
command = ArchiveCommandFunc()
}
// 将命令入队
commands <- command
}
}()
for {
select {
case c := <-commands:
c()
case <-time.After(1 * time.Second):
fmt.Println("timeout 1s")
return
}
}
}