专栏首页架构师修炼设计模式实战-命令模式

设计模式实战-命令模式

1、定义

命令模式(Command Pattern)又称为行动(Action)模式或交易(Transaction)模式。

命令模式的英文定义是:

Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations.

意思是:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。

简单来说,命令模式就是将发送者、接收者和调用命令封装成对象,客户端调用的时候可以选择不同的对象,从而实现发送者和接收者的完全解耦。

2、组成角色

命令模式包含如下角色:

  • 命令接口(Command)角色:该角色声明一个接口,定义需要执行的命令;
  • 具体命令实现类(Concrete Command)角色:该角色定义一个接收者和行为之间的弱耦合,实现命令方法,并调用接收者的相应操作;
  • 调用者(Invoker)角色:该角色负责调用命令对象执行请求;
  • 接收者(Receiver)角色:该角色负责具体实施和执行请求动作(方法);
  • 客户端(Client)角色:串连执行整个流程。

角色关系类图如下:

3、命令模式代码实现

3.1 接收者

// 接收者
class Receiver {
    public void doSomething() {
        System.out.println("执行业务逻辑");
    }
}

3.2 命令对象

// 命令接口
interface Command {
    void execute();
}
// 具体命令类
class ConcreteCommand implements Command {
    private Receiver receiver;
    public ConcreteCommand(Receiver receiver) {
        this.receiver = receiver;
    }
    public void execute() {
        this.receiver.doSomething();
    }
}

3.3 请求者

// 请求者类
class Invoker {
    // 持有命令对象
    private Command command;
    public Invoker(Command command) {
        this.command = command;
    }
    // 请求方法
    public void action() {
        this.command.execute();
    }
}

3.4 客户端

// 客户端
class Client {
    public static void main(String[] args) {
        // 创建接收者
        Receiver receiver = new Receiver();
        // 创建命令对象,设定接收者
        Command command = new ConcreteCommand(receiver);
        // 创建请求者,把命令对象设置进去
        Invoker invoker = new Invoker(command);
        // 执行方法
        invoker.action();
    }
}

通过代码我们可以看到,命令模式把一条命令分为四步,先定义接收者,再创建执行命令对象,再创建请求者,最后执行命令方法。它的耦合度要比把所有的操作都封装到一个类中要低的多,而这也正是命令模式的精髓所在:把命令的调用者与执行者分开,使双方不必关心对方是如何操作的。

4、优缺点

命令模式的优点:

  • 类间解耦:调用者角色与接收者角色之间没有任何依赖关系,调用者实现功能时只需要调用 Command 中的 execute() 方法即可,不需要了解是哪个接收者执行;
  • 可扩展性:Command 的子类可以非常容易地扩展,而调用者 Invoker 和高层次的模块 Client 不产生严重的代码耦合。

命令模式的缺点:

  • 使用命令模式会导致系统有过多的具体命令类,因为针对每一个命令都需要设计一个具体命令类。

5、应用场景

命令模式的典型应用场景如下:

  • 系统需要支持命令的撤销(undo),命令对象可以把状态存储起来,等到客户端需要撤销时,可以调用 undo() 方法,将命令所产生的效果撤销;
  • 系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作;
  • 系统需要将一组操作组合在一起,使用命令模式来实现,可以很方便的增加新的命令。

6、使用实例

以生活中的看电视为例,其中遥控器就是命令发送者,电视就是请求接收者,分别对应:开机、关机、切换频道三个命令,实现代码如下。

6.1 定义遥控功能(命令接口和实现类)

interface Command {
    void execute();
}
// 打开电视(命令)
class OpenTvCommand implements Command {
    private TV tv;
    public OpenTvCommand(TV tv) {
        this.tv = tv;
    }
    public void execute() {
        tv.open();
    }
}
// 更换电视频道(命令)
class ChangeTvCommand implements Command {
    private TV tv;
    public ChangeTvCommand(TV tv) {
        this.tv = tv;
    }
    public void execute() {
        tv.change();
    }
}
// 关闭电视(命令)
class CloseTvCommand implements Command {
    private TV tv;
    public CloseTvCommand(TV tv) {
        this.tv = tv;
    }
    public void execute() {
        tv.close();
    }
}

6.2 接收者(执行命令)

// 电视机的具体动作
class TV {
    public void open() {
        System.out.println("打开电视机");
    }
    public void close() {
        System.out.println("关闭电视机");
    }
    public void change() {
        System.out.println("切换电视频道");
    }
}

6.3 执行者(发起执行命令对象)

// 遥控器
class TvRemote {
    private Command openTvCommand;
    private Command closeTvCommand;
    private Command changeTvCommand;
    public TvRemote(Command openTvCommand, Command closeTvCommand, Command changeTvCommand) {
        this.openTvCommand = openTvCommand;
        this.closeTvCommand = closeTvCommand;
        this.changeTvCommand = changeTvCommand;
    }
    // 打开电视
    public void open() {
        openTvCommand.execute();
    }
    // 关闭电视
    public void close() {
        closeTvCommand.execute();
    }
    // 换频道
    public void change() {
       changeTvCommand.execute();
    }
}

6.4 客户端(调用)

class Client{
    public static void main(String[] args) {
        TV tv = new TV();
        Command openTvCommand = new OpenTvCommand(tv);
        Command closeTvCommand = new CloseTvCommand(tv);
        Command changeTvCommand = new ChangeTvCommand(tv);
        TvRemote control = new TvRemote(openTvCommand,closeTvCommand,changeTvCommand);
        control.open();
        control.change();
        control.close();
    }
}

7、总结

命令模式是通过封装命令类来实现解耦调用者(发送命令)和接收者(执行命令),它的优点是可扩展性好,缺点是需要为不同的命令需要制定单独的命令类。你还知道哪些命令模式在生活中的例子呢?

若将上面代码改为使用循环的方式,又该如何修改呢?大家可以思考一下。

本文分享自微信公众号 - 架构师修炼(jiagouxiulian),作者:架构师修炼

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-04-21

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Redis 哨兵机制以及底层原理深入解析,这次终于搞清楚了

    前面我们基于实际案例搭建了缓存高可用方案(分布式缓存高可用方案,我们都是这么干的)同时提到了redis主从架构下是如何保证高可用的,讲到了它是通过redis s...

    架构师修炼
  • 设计模式实战-状态模式,让代码更清爽简洁

    状态模式(Allow an object to alter its behavior when its internal state changes.The o...

    架构师修炼
  • 设计模式实战-中介模式,为你牵桥搭线

    中介者?其实生活中大家再熟悉不过了这个词,我们熟悉的黄牛、房产中介等就是充当中介的角色,将我们的买票、购房等的需求自身消化再代为办理。又比如说中间件,马老师很忙...

    架构师修炼
  • 【一起学系列】之命令模式:封装一个简单Jedis?

    将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

    Kerwin
  • Java 接口——面向对象的精髓

    接口有何用?面试宝典上背下来的总结,你真的明白吗? 接口&工厂方法 其实很简单>。<~ 什么是接口 先看看生活中的接口,比如USB接口。

    Java团长
  • 2019-11-19 一段可以把MacOS搞重启的java代码

    Albert陈凯
  • Android编程设计模式之命令模式详解

    本文实例讲述了Android编程设计模式之命令模式。分享给大家供大家参考,具体如下:

    砸漏
  • Android编程设计模式之中介者模式详解

    本文实例讲述了Android编程设计模式之中介者模式。分享给大家供大家参考,具体如下:

    砸漏
  • 缘分一道桥——桥接模式

    桥接模式是一种很实用的结构型设计模式,它是将抽象部分与它的实现部分分离,使他们都可以独立地变化。 首先介绍一个标准的桥接模式的使用场景: 如果我想买汽车Ca...

    文彬
  • 创建型模式:建造者模式

    Separate the construction of a complex object from its representation so that th...

    LieBrother

扫码关注云+社区

领取腾讯云代金券