设计模式-命令模式(Go语言描述)

在上一篇博客设计模式-单例模式(Go语言描述)中我们介绍了在golang中如何去实现单例模式,在文章的最后我们也介绍到了golang独有的一种实现单例的方式-sync.Once.Do(),可以让golang轻松的实现可以应对并发请求的单利.今天我们继续探索设计模式,来介绍一下命令模式的实现.

说起命令,大家第一反应可能就是我们平时敲的各种命令,啪啪啪几行命令下去就可以完成一些功能,在看到命令模式这个词后,可能大家也会和我一样认为这里的命令就是执行一些简单任务的功能,然而并不是,这里的命令更多的像是我们发出的请求或者电视遥控器的按键.这样吧,咱们先来看看命令模式的定义,然后再从生活中找实际的例子对比一下.

命令模式是将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作.命令模式是一种对象行为型模式,其别名为动作模式或事务模式.

在命令模式中有如下几个角色:

Command: 命令

Invoker: 调用者

Receiver: 接受者

Client: 客户端

他们的关系可以这么来描述:

客户端通过调用者发送命令,命令调用接收者执行相应操作.

其实命令模式也很简单,不过不知道大家发现没有,在上述描述中调用者和接收者并不知道对方的存在,也就是说他们之间是解耦合的.

还是用遥控器的例子来解释一下吧,遥控器对应上面的角色就是调用者,电视就是接收者,命令呢?对应的就是遥控器上的按键,最后客户端就是我们人啦,当我们想打开电视的时候,就会通过遥控器(调用者)的电源按钮(命令)来打开电视(接收者),在这个过程中遥控器是不知道电视的,但是电源按钮是知道他要控制谁的什么操作.

… 哎呀, 语文不太好,突然发现越描述越不容易让人理解了呢? 还是用代码来实现一下上面遥控器的例子吧.我们对照着上面的角色一个个的去实现.

接收者,也就是一台大大的电视,

// receiver
type TV struct{}

func (p TV) Open() {
    fmt.Println("play...")
}

func (p TV) Close() {
    fmt.Println("stop...")
}

这台电视弱爆了,只有打开和关闭两个功能,对应的就是上面代码中的OpenClose两个方法,虽然很简单,不过我们确实造出了一台电视(估计还是彩色的).

下面,我们再来实现命令,也就是遥控器上的按键,因为电视只有打开和关闭功能,所以按键我们也只提供两个,多了用不上.

// command
type Command interface {
    Press()
}

type OpenCommand struct {
    tv TV
}

func (p OpenCommand) Press() {
    p.tv.Open()
}

type CloseCommand struct {
    tv TV
}

func (p CloseCommand) Press() {
    p.tv.Close()
}

首先我们定义了一个命令接口,只有一个方法就是Press,当我们按下按键时会去调用这个方法,然后我们果然只实现了两个按键,分别是OpenCommandCloseCommand,这两个实现中都保存着一台电视的句柄,并且在Press方法中根据功能去调用了这个tv的相应方法来完成正确的操作.

还有什么我们没有实现? 调用者,也就是遥控器了,来看看遥控器怎么实现吧.

// sender
type Invoker struct {
    cmd Command
}

func (p *Invoker) SetCommand(cmd Command) {
    p.cmd = cmd
}

func (p Invoker) Do() {
    p.cmd.Press()
}

在遥控器中我们有一个Command类型的变量,并且提供了SetCommand方法来设置命令,那我们按下遥控器上的键对应哪个方法呢?看看Do方法,我们直接调用了命令的Press方法,这个时候客户端(就是我们人)拿起遥控器,瞅准了打开按钮(SetCommand),并且按钮该键(Do),按键被按下(Press),电视打开了(Open).

那么最后,来看看客户端怎么去调用吧,

func main() {
    var tv TV
    openCommand := OpenCommand{tv}
    invoker := Invoker{openCommand}
    invoker.Do()

    invoker.SetCommand(CloseCommand{tv})
    invoker.Do()
}

命令模式实现起来也很简单,它降低了我们这个系统的耦合度,并且我们还可以任意拓展命令使用而不需要修改代码,开闭原则体现的淋漓尽致.

现在大家应该对命令模式有了一个直观的认识吧, 不过大家有没有发现一个问题,我们的遥控器在使用某个命令的时候只有set后才能用,也就是说每次只能使用一个命令,那有没有更好的办法来预装命令呢?肯定是有的,这就是我们接下来要介绍的复合命令,也叫做宏命令,复合命令其实说白了就是将多个命令保存起来,通过遍历这个集合来分别调用各个命令.

func NewOpenCloseCommand() *OpenCloseCommand {
    var openClose *OpenCloseCommand = &OpenCloseCommand{}
    openClose.cmds = make([]Command, 2)
    return openClose
}

type OpenCloseCommand struct {
    index int
    cmds  []Command
}

func (p *OpenCloseCommand) AddCommand(cmd Command) {
    p.cmds[p.index] = cmd
    p.index++
}

func (p OpenCloseCommand) Press() {
    for _, item := range p.cmds {
        item.Press()
    }
}

func main() {
  var openClose *OpenCloseCommand = NewOpenCloseCommand()
  openClose.AddCommand(OpenCommand{tv})
  openClose.AddCommand(CloseCommand{tv})
  invoker.SetCommand(openClose)
  invoker.Do()
}

我们定义了一个OpenCloseCommand,这里面用一个切片来保存各个命令,并通过AddCommand方法来王里面添加命令,OpenCloseCommand实现了Press方法,所以本质上他也是一个命令,我们调用他的Press方法来遍历保存的Command,并调用每一个的Press方法.

到这里,命令模式我们就介绍完了,命令模式具有很高的扩展性,遵循了开闭原则,所以减少了修改代码引入bug的可能性,快来想一想你的代码中哪些地方可以用命令模式来解决吧.

时间: 2024-08-03 15:24:49

设计模式-命令模式(Go语言描述)的相关文章

设计模式-代理模式(Go语言描述)

大家好, 我是大帅哥,由于最近又变帅了, 所以我决定由我的代理人小帅哥来写这篇博客,不过内容可以放心,还是由我来完成,小帅哥的活就是将我的内容替我发出来罢了. &_& 还是进入正题吧, 今天这篇博客我们继续来学习设计模式, 在学习了上面几个设计模式之后大家有没有发现设计模式其实很简单, 难道地方是何时选用合适的模式, 这也是设计模式最重要的地方, 不能我们学会的设计模式就可以乱用. 明白就好-- 下面我们就开始介绍一个新的设计模式-代理模式. 模式定义 什么是代码模式? 其实文章刚开始的段

设计模式-模板方法模式(Go语言描述)

这篇文章我们还是继续我们的设计模式系列, 今天我们带来的一个全新的设计模式在实际开发中大家肯定都遇到过, 可能大家只是不知道它叫模板方法模式而已, 今天我们就来详细的说一下什么是模板方法模式,已经该模式如何运用. 至于什么是模板方法模式, 我们还是老规矩, 先来个定义, 然后上张类图更加直观的看一下. 定义 模板方法模式定义了一个算法的步骤,并允许子类别为一个或多个步骤提供其实践方式.让子类别在不改变算法架构的情况下,重新定义算法中的某些步骤. 这个定义还是非常不错的, 至少认真读2遍还是可以理

设计模式之适配者模式——Java语言描述 | Amos H's blog

适配器模式是作为两个不兼容的接口之间的桥梁.这种类型的设计模糊属于结构性模式,它结合了两个独立接口的功能 概念阐述 使用适配器模式可以解决在软件系统中,将一些旧的类放入新环境中,但是新环境要求的接口旧的类不能满足的情况. 使用情形: 系统需要使用旧的类,但是此类的接口不符合系统的需要 需要建立一个可以重复使用的类,用于一些彼此之间没有太大关系的一些类 通过接口转换,将一个类插入到另一个类中 优点: 可以让任何两个没有关联的类一起运行 提高了类的复用 增加了类的透明度 灵活性好 缺点: 过多的使用

设计模式 - 命令模式(command pattern) 具体解释

命令模式(command pattern) 详细解释 本文地址: http://blog.csdn.net/caroline_wendy 命令模式(command pattern) : 将请求封装成对象, 以便使用不同的请求\队列\日志来參数化其它对象. 命令模式也能够支持撤销操作. 简单的命令模式的实现: 1. 详细的类, 每个类都有特定的方法: /** * @time 2014年6月9日 */ package command; /** * @author C.L.Wang * */ publ

设计模式 - 命令模式(command pattern) 详解

命令模式(command pattern) 详解 本文地址: http://blog.csdn.net/caroline_wendy 命令模式: 将请求封装成对象, 以便使用不同的请求\队列\日志来参数化其他对象. 命令模式也支持可撤销操作. 命令模式: 调用者(Invoker); 命令(Command): 可执行方法(execute), 具体命令(Concrete Command); 接受者(Receiver): 调用命令(Set Command); 具体方法: 1. 具体对象. /** *

设计模式 - 命令模式(command pattern) 多命令 详解

命令模式(command pattern) 多命令 详解 本文地址: http://blog.csdn.net/caroline_wendy 参考命令模式: http://blog.csdn.net/caroline_wendy/article/details/31379977 具体步骤: 1. 多命令, 把未使用的命令, 初始化为空对象(NoCommand), 根据参数(slot), 选择输出命令. /** * @time 2014年6月16日 */ package command; /**

设计模式 - 命令模式(command pattern) 撤销(undo) 详解

命令模式(command pattern) 撤销(undo) 详解 本文地址: http://blog.csdn.net/caroline_wendy 参考命令模式: http://blog.csdn.net/caroline_wendy/article/details/31379977 命令模式可以用于执行撤销(undo)操作. 具体方法: 1. 对象类中需要保存状态, 如level. package command; public class CeilingFan { String loca

设计模式 - 命令模式(command pattern) 宏命令(macro command) 详解

命令模式(command pattern) 宏命令(macro command) 详解 本文地址: http://blog.csdn.net/caroline_wendy 参考: 命名模式(撤销): http://blog.csdn.net/caroline_wendy/article/details/31419101 命令模式可以执行宏命令(macro command), 即多个命令的组合操作. 具体方法:  1. 其余代码与命令(撤销)一致 2. 添加宏命令(macro command),

设计模式--命令模式(Command)

基本概念: Command模式也叫命令模式 ,是行为设计模式的一种.Command模式通过被称为Command的类封装了对目标对象的调用行为以及调用参数,命令模式将方法调用给封装起来了. 命令模式的几个角色: Command: 抽象命令类 ConcreteCommand: 具体命令类 Invoker: 调用者 Receiver: 接收者 Client:客户类 命令模式的优缺点: 优点 1. 降低了系统耦合度 2. 新的命令可以很容易添加到系统中去. 缺点 使用命令模式可能会导致某些系统有过多的具