命令模式.

一、概念

  • 命令模式:将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。
  • 角色:  1、命令(Command):为所有命令声明了一个接口。调用命令对象的 execute()方法,就可以让接收者进行相关的操作。这个接口也具备一个 undo() 方法。  2、具体命令(ConcreteCommand):实现命令接口,定义了动作和接收者之间的绑定关系。调用者只要调用 execute() 就可以发出请求,然后由 ConcreteCommand 调用接收者的一个或多个动作。  3、请求者(Invoker):持有一个命令对象,有一个行动方法,在某个时间点调用命令对象的 execute() 方法,将请求付诸实行。  4、接收者(Receiver):接收者知道如何进行必要的动作,实现这个请求。任何类都可以当接收者。  5、客户端(Client):创建一个具体命令(ConcreteCommand)对象并确定其接收者,包括把其他角色串连在一起。

二、Demo 实现

Topic:我们要制作一个简易的遥控器,有两个控制灯开关的按钮,并有一个操作回退按钮。

1、接收者

 首先,我们先来定义一个接收者的角色,也就是最后执行动作的那个对象 —— Light.java,控制着灯的开启和关闭。

public class Light {

    public void on() {
        System.out.println("灯亮了...");
    }

    public void off() {
        System.out.println("灯暗了...");
    }
}

2、命令

 现在,我们要定义一个命令角色。一般是一个接口,为所有的命令对象声明一个接口,规范将要进行的命令操作。

public interface Command {
    /**
     * 执行命令
     */
    void execute();

    /**
     * 撤销命令
     */
    void undo();
}

3、具体命令

 有了命名角色后,我们要构建具体命令角色。具体命令实现了命令接口,定义了动作和接收者之间的绑定关系。这里,我们有两个具体命令对象—— LightOnCommand.java(开灯命令)、LightOffCommand.java(关灯命令)

public class LightOnCommand implements Command {

    private Light light;

    public LightOnCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.on();
    }

    @Override
    public void undo() {
        light.off();
    }
}
public class LightOffCommand implements Command {

    private Light light;

    public LightOffCommand(Light light) {
        this.light = light;
    }

    @Override
    public void execute() {
        light.off();
    }

    @Override
    public void undo() {
        light.on();
    }
}

4、请求者

 前面,我们定义了动作的接收方和联系中介 —— 命名对象。现在,我们要着手构建请求者角色了。请求者持有一个命令对象,有一个行动方法。它会在某个时间点执行行动方法,但不关心是谁具体执行了这个动作。

public class RemoteInvoker {

    /**
     * 开关命令数组,模拟有很多对开关数组
     */
    private Command[] onCommands;
    private Command[] offCommands;
    /**
     * 撤销(回退)命令
     */
    private Command undoCommand;

    public RemoteInvoker(int length) {
        // 有几组开关,就设置多少数组
        onCommands = new Command[length];
        offCommands = new Command[length];
        // 把每个命令初始化成空命令,避免空指针异常
        Command noCommand = new NoCommand();
        undoCommand = noCommand;
        for (int i = 0; i < length; i++) {
            onCommands[i] = noCommand;
            offCommands[i] = noCommand;
        }
    }

    /**
     * @Description 设置命令对象
     * @date 2018/11/29 09:15
     * @param slot 遥控器的位置
     * @param onCommand 开的命令
     * @param offCommand 关的命令
     * @return void
     */
    public void setCommond(int slot, Command onCommand, Command offCommand) {
        onCommands[slot] = onCommand;
        offCommands[slot] = offCommand;
    }


    public void onButton(int slot) {
        onCommands[slot].execute();
        //为撤销(回退)按钮记录动作
        undoCommand = onCommands[slot];
    }

    public void offButton(int slot) {
        offCommands[slot].execute();
        //为撤销(回退)按钮记录动作
        undoCommand = offCommands[slot];
    }

    public void undoButton() {
        undoCommand.undo();
    }
}

5、客户端

 前面,我们定义好了请求者、接收者已经两者之间的联系中介 —— 命令对象。但是这几个角色对象之间都是松耦合的,还没有一个具体动作的流程,现在我们利用客户端角色把整个动作流程串联在一起。

public class RemoteClient {

    public static void main(String[] args) {
        // 1、创建接收者
        Light light = new Light();
        // 2、创建命令对象
        LightOnCommand lightOnCommand = new LightOnCommand(light);
        LightOffCommand lightOffCommand = new LightOffCommand(light);
        // 3、创建一组开关并用命令对象装载它
        RemoteInvoker invoker = new RemoteInvoker(1);
        invoker.setCommond(0, lightOnCommand, lightOffCommand);
        // 4、测试
        invoker.onButton(0);
        invoker.offButton(0);
        invoker.undoButton();
    }
}

三、总结

  • 命令模式将发出请求的对象和执行请求的对象解耦,在被解耦的两者之间是通过命令对象进行沟通的。
  • 一个命令对象通过在特定接收者上绑定一组动作来封装一个请求。要达到这一点,命令对象将接收者和动作封装进对象中,这个对象只暴露出一个 execute() 方法,当此方法被调用时,接收者就会进行这些动作。从外面来看,其他对象不知道究竟哪个接收者进行了哪些操作,只知道如果调用 execute() 方法,请求的目的就可以达到。
  • 当你不想返回一个有意义的对象时,空对象就很有用。这样,我们就可以把处理 null 的责任转移给空对象,甚至有些时候,空对象本身也被视为一种设计模式。
  • 我们还可以把一堆命令组装起来拼成一个命令,称为宏命令。宏命令是命令的一种延伸,允许调用一系列的命令。包括一系列的执行和撤销动作。
  • 适用场景:  1、命令的发送者和命令执行者有不同的生命周期。命令发送了并不是立即执行。  2、命令需要进行各种管理逻辑,比如:对多个命令的统一控制。  3、需要支持撤消/重做操作。

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏cloudskyme

gsoap开发webservice

gSOAP编译工具提供了一个SOAP/XML 关于C/C++ 语言的实现,从而让C/C++语言开发web服务或客户端程序的工作变得轻松了很多。绝大多数的C++w...

35460
来自专栏JetpropelledSnake

RESTful源码笔记之RESTful Framework的Mixins小结

本篇对drf中的mixins进行简要的分析总结。 Mixins在drf中主要配合viewset共同使用,实现http方法与mixins的相关类与方法进行关联。

7910
来自专栏积累沉淀

干货--Redis 30分钟快速入门

一、 redis环境搭建 1.简介        redis是一个开源的key-value数据库。它又经常被认为是一个数据结构服务器。因为它的value不仅...

338100
来自专栏北京马哥教育

Ansible 详细用法说明(二)

例:获取某台主机的变量 ansible 10.1.6.68 -m setup ===================================== sc...

38250
来自专栏好好学java的技术栈

“面试不败计划”:Java多线程和并发基础面试问答

多线程和并发问题是Java技术面试中面试官比较喜欢问的问题之一。在这里,从面试的角度列出了大部分重要的问题,但是你仍然应该牢固的掌握Java多线程基础知识来对应...

9120
来自专栏北京马哥教育

Linux 中命令链接操作符,让你的代码更简洁!

Linux命令中链接的意思是,通过操作符的行为将几个命令组合执行。Linux中的链接命令,有些像你在shell中写短小的shell脚本,并直接在终端中执行。链接...

8920
来自专栏林欣哲

汇编程序

ISA指令集是由0和1组成的机器语言,难以记忆和阅读,因此人们发明汇编程序帮助记忆。 汇编基本算是和机器指令一一对应的关系,可以认为是给机器指令的每个部分分别起...

34960
来自专栏Java技术栈

Java高级篇——深入浅出Java类加载机制

类加载器 简单讲,类加载器ClassLoader的功能就是负责将class文件加载到jvm内存。 类加载器分类 从虚拟机层面讲分为两大类型的类加载器,一是Boo...

27570
来自专栏抠抠空间

python并发编程之多进程(实现)

一、multipricessing模块的介绍      python中的多线程无法利用多核优势,如果想要充分的使用多核CPU资源,在python中大部分情况下需...

490110
来自专栏木木玲

设计模式 ——— 职责链模式

15330

扫码关注云+社区

领取腾讯云代金券