专栏首页架构探险之道[设计模式] 命令模式

[设计模式] 命令模式

[设计模式] 命令模式

手机用户请 横屏获取最佳阅读体验, REFERENCES中是本文参考的链接,如需要链接和更多资源,可以关注其他博客发布地址。

平台

地址

CSDN

https://blog.csdn.net/sinat_28690417

简书

https://www.jianshu.com/u/3032cc862300

个人博客

https://yiyuery.github.io/NoteBooks/

命令模式 将请求封装成对象,这可以让你使用不同的请求、队列或者是日志请求来参数化其他对象。命令模式一般支持定义撤销操作。

命令模式实现

简单命令

首先来让我们实现一个能开灯的遥控命令场景。

//简单命令接口定义,内部含有一个执行方法
public interface Command {

    /**
     * 执行
     */
    void execute();
}

定义一个开灯的命令接口实现:

public class LightOnCommand implements Command {
    /**
     * 执行
     */
    @Override
    public void execute() {
        System.out.println("The light has been turned on!");
    }
}

定义一个遥控器:

@Data
public class SimpleRemoteController {

    /**
     * 命令
     */
    private Command command;

    /**
     * 遥控按钮被按下时,执行命令
     */
    public void buttonWasPressed(){
        command.execute();
    }
}

Ex.1

/**
 * 测试遥控器按下开灯的按钮
 */
@Test
public void testX1(){
    SimpleRemoteController remoteController = new SimpleRemoteController();
    remoteController.setCommand(new LightOnCommand());
    //按键
    remoteController.buttonWasPressed();

    //The light has been turned on!
}

批量命令

如果是需要一组命令的执行呢?

补充定义一个命令 NoCommand,用于默认命令的信息打印。

public class NoCommand implements Command {
    /**
     * 执行
     */
    @Override
    public void execute() {
        System.out.println("Command has not been defined!");
    }
}
public class MultipleRemoteController {

    Command[] commands;

    public MultipleRemoteController(int num) {
        commands = new Command[num];
        for (Command command : commands) {
            command = new NoCommand();
        }
    }

    public void setCommand(int index,Command command){
        commands[index] = command;
    }

    /**
     * 遥控按钮被按下时,执行命令
     */
    public void buttonWasPressed(int index){
        commands[index].execute();
    }
}

Ex.2

/**
 * 测试遥控器按下开灯的按钮
 */
@Test
public void testX2(){
    MultipleRemoteController remoteController = new MultipleRemoteController(2);
    remoteController.setCommand(0,new LightOnCommand());
    remoteController.setCommand(1,new TVOnCommand());
    //按键
    remoteController.buttonWasPressed(0);
    remoteController.buttonWasPressed(1);

    //The light has been turned on!
    //TV has been turned on!
}

简单撤销

定义两组命令,每次在执行开启操作前将对应的关闭操作写入缓存变量,在执行撤销方法时,执行即可。

//为了实现代码复用(可以暂时不关注,只关注于宏命令的使用即可),将遥控器的公共部分抽离到一个抽象基类中:
public abstract class AbstractRemoteController<T,K extends Command> {

    protected K undoCommand;

    /**开启操作命令数组*/
    protected K[] onCommands;
    /**关闭操作命令数组*/
    protected K[] offCommands;

    public boolean supportUndo(){
        return true;
    }

    /**
     * 设置命令
     * @param index
     * @param onCommand
     * @param offCommand
     */
    public abstract void setCommand(int index, T onCommand, T offCommand);


    /**
     * 遥控按钮被按下时,执行命令
     */
    public abstract void buttonWasPressed(int index);

    /**
     * 开启操作的撤销按钮
     */
    public  void undoButtonWasPressed(){
        //该基类的定义使用了模版模式,是否支持undo操作由子类决定
        if(supportUndo()){
            undoCommand.execute();
        }
    }
}

//支持简单撤销的遥控器
public class SupportUndoRemoteController extends AbstractRemoteController<Command,Command> {

    public SupportUndoRemoteController(int num) {
        onCommands = new Command[num];
        offCommands = new Command[num];
        for (int i = 0; i < num; i++) {
            onCommands[i] = new NoCommand();
            offCommands[i] = new NoCommand();
        }
    }

    @Override
    public void setCommand(int index,Command onCommand,Command offCommand){
        onCommands[index] = onCommand;
        offCommands[index] = offCommand;
    }



    /**
     * 遥控按钮被按下时,执行命令
     */
    @Override
    public void buttonWasPressed(int index){
        onCommands[index].execute();
        undoCommand = offCommands[index];
    }

}

Ex.3

/**
 * 支持开启操作的简单撤销
 */
@Test
public void testX3(){
    SupportUndoRemoteController supportUndoRemoteController = new SupportUndoRemoteController(2);

    //设置灯的开和关命令
    supportUndoRemoteController.setCommand(0,new LightOnCommand(),new LightOffCommand());
    //设置电视的开和关命令
    supportUndoRemoteController.setCommand(1,new TVOnCommand(),new TVOffCommand());

    //先开电视后开灯
    supportUndoRemoteController.buttonWasPressed(1);
    supportUndoRemoteController.buttonWasPressed(0);

    //开电视后撤销再开灯
    supportUndoRemoteController.buttonWasPressed(1);
    supportUndoRemoteController.undoButtonWasPressed();
    supportUndoRemoteController.buttonWasPressed(0);

    //TV has been turned on!
    //The light has been turned on!

    //TV has been turned on!
    //TV has been turned off!
    //The light has been turned on!
}

宏命令

定义一组命令的执行,类似于 MicroSoft 常用的宏定义函数一样

假定我们定义一个宏命令实现回家后的一个智能操作:包含开灯和开电视,并要求它支持撤销能力。

//宏命令
public class MacroCommand implements Command {

    Command[] commands;

    public MacroCommand(Command[] commands) {
        this.commands = commands;
    }

    /**
     * 执行
     */
    @Override
    public void execute() {
        for (Command command : commands) {
            command.execute();
        }
    }
}

//支持宏命令的遥控器
public class SupportMacroRemoteController extends AbstractRemoteController<MacroCommand,Command> {

    public SupportMacroRemoteController(int num) {
        onCommands= new Command[num];
        offCommands = new Command[num];
        for (int i = 0; i <num ; i++) {
            onCommands[i]= new NoCommand();
            offCommands[i]= new NoCommand();
        }
    }

    @Override
    public void setCommand(int index, MacroCommand onMacroCommand, MacroCommand offMacroCommand){
        onCommands[index] = onMacroCommand;
        offCommands[index] = offMacroCommand;
    }

    /**
     * 遥控按钮被按下时,执行命令
     *
     * @param index
     */
    @Override
    public void buttonWasPressed(int index) {
        onCommands[index].execute();
        undoCommand = offCommands[index];
    }

    @Override
    public boolean supportUndo() {
        return true;
    }
}
//开电视
public class TVOnCommand implements Command {
    /**
     * 执行
     */
    @Override
    public void execute() {
        System.out.println("TV has been turned on!");
    }
}

Ex.4

/**
 * 宏命令测试
 */
@Test
public void testX4(){
    SupportMacroRemoteController supportUndoRemoteController = new SupportMacroRemoteController(2);
    Command[] partyOn = new Command[]{new LightOnCommand(),new TVOnCommand()};
    Command[] partyOff = new Command[]{new LightOffCommand(),new TVOffCommand()};
    supportUndoRemoteController.setCommand(0,new MacroCommand(partyOn),new MacroCommand(partyOff));

    //开Party后撤销
    supportUndoRemoteController.buttonWasPressed(0);
    supportUndoRemoteController.undoButtonWasPressed();

    //The light has been turned on!
    //TV has been turned on!
    //The light has been turned off!
    //TV has been turned off!
}

标准撤销方式

针对于命令场景的撤销操作,其实我们可以直接定义一个 undo操作接口在 Command中,此处考虑到演示的代码结构,我们在补充一个接口继承 Command来实现

public interface SupportUndoCommand extends Command {

    /**
     * 撤销操作
     */
    void undo();
}

对应的undo方式执行也调整下实现:

//新的开灯方法
public class LightOnCommand2 implements SupportUndoCommand {
    /**
     * 执行
     */
    @Override
    public void execute() {
        System.out.println("The light has been turned on!");
    }

    /**
     * 撤销操作
     */
    @Override
    public void undo() {
        System.out.println("The light turn on has been canceled!");
    }
}
//新的开电视方法
public class TVOnCommand2 implements SupportUndoCommand {
    /**
     * 执行
     */
    @Override
    public void execute() {
        System.out.println("TV has been turned on!");
    }

    /**
     * 撤销操作
     */
    @Override
    public void undo() {
        System.out.println("TV turn on has been canceled!");
    }
}
//支持undo的遥控器
public class NewSupportUndoRemoteController extends AbstractRemoteController<SupportUndoCommand,SupportUndoCommand> {

    public NewSupportUndoRemoteController(int num) {
        onCommands = new SupportUndoCommand[num];
        for (int i = 0; i < num; i++) {
            onCommands[i] = new NoUndoCommand();
        }
    }

    @Override
    public void setCommand(int index, SupportUndoCommand onCommand, SupportUndoCommand offCommand){
        onCommands[index] = onCommand;
    }



    /**
     * 遥控按钮被按下时,执行命令
     */
    @Override
    public void buttonWasPressed(int index){
        onCommands[index].execute();
    }

    /**
     * 开启操作的撤销按钮
     */
    @Override
    public void undoButtonWasPressed(){
        for (SupportUndoCommand onCommand : onCommands) {
            onCommand.undo();
        }
    }

}

Ex.5

/**
    * 宏命令 & 标准撤销测试
    */
@Test
public void testX5(){

    NewSupportUndoRemoteController newSupportUndoRemoteController = new NewSupportUndoRemoteController(1);
    SupportUndoCommand[] partyOn2 = new SupportUndoCommand[]{new LightOnCommand2(),new TVOnCommand2()};
    SupportUndoCommand[] partyOff2 = new SupportUndoCommand[]{new LightOffCommand2(),new TVOffCommand2()};
    newSupportUndoRemoteController.setCommand(0,new MacroCommand2(partyOn2),new MacroCommand2(partyOff2));

    //开Party后撤销
    newSupportUndoRemoteController.buttonWasPressed(0);
    newSupportUndoRemoteController.undoButtonWasPressed();

    //The light has been turned on!
    //TV has been turned on!
    //The light turn on has been canceled!
    //TV turn on has been canceled!
}

总结

模式特点

  • 将发出请求的对象和执行的对象解耦
  • 被解耦的两者之间是通过命令对象之间进行沟通的,命令对象封装了接受者和一个/组动作
  • 调用者通过调用命令对象的 execute发出请求,后者根据这个进行响应
  • 调用者可以接受命令当做传入的参数,甚至在运行时动态地进行
  • 命令还支持撤销操作的定义,做法是实现一个 undo方法来回到 execute被执行前的状态
  • 宏命令是命令的一种简单延伸,允许调用多个命令

敲黑板 !!!!

  • 命令模式致力于发送请求和执行请求的对象解耦操作!

本文分享自微信公众号 - 架构探险之道(zacsnz1314),作者:MasterYang

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

原始发表时间:2019-08-25

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • [设计模式] 适配器模式 + 外观模式

    顾名思义, ProductV2API是新的API接口, ProductV1API为历史API接口,实现类 HisAPIAdaptee为历史API的实现类,即被适...

    架构探险之道
  • Java 中的 "extends" 关键字

    继承之后,子类实例化之前会先执行被重载的父类方法overrideMe(),此时子类的实例化尚未完成,静态块也未执行,所以虽然是final修饰的字段,date变量...

    架构探险之道
  • [Spring Boot] Spring Boot 装配实现原理

    本文就 Spring Boot 的配置装配实现方式做了介绍,主要是常用的模式注解、@EnableXXX注解、条件注解和自动装配是如何实现的。

    架构探险之道
  • Android通过Socket与服务器之间进行通信的示例

    1)、创建要操作的实体类,实现 Parcelable 接口,以便序列化/反序列化 2)、新建 aidl 文件夹,在其中创建接口 aidl 文件以及实体类的映射...

    砸漏
  • 【一起学系列】之命令模式:封装一个简单Jedis?

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

    Kerwin
  • java编程_socket_套接字_网络编程_简易的GUI多线程聊天程序

    ===============================================================

    Hongten
  • 设计模式(2):观察者模式-2 (委托、事件)

    概念:对某个方法引用的一种引用类型变量。注意看概念, 委托是一种引用类型变量, 类型变量,因此可以将委托当作方法的参数进行传递。 通俗点来讲,委托有点像方法的快...

    用户2434869
  • 高性能NIO框架Netty-对象传输

    上篇文章高性能NIO框架Netty入门篇我们对Netty做了一个简单的介绍,并且写了一个入门的Demo,客户端往服务端发送一个字符串的消息,服务端回复一个字符串...

    猿天地
  • 常见设计模式面试必备

    1、单例类只有一个实例对象; 2、该单例对象必须由单例类自行创建; 3、单例类对外提供一个访问该单例的全局访问点;

    挨踢小子部落阁
  • 《你必须知道的.NET》读书笔记二:小OO有大原则

    此篇已收录至《你必须知道的.Net》读书笔记目录贴,点击访问该目录可以获取更多内容。

    Edison Zhou

扫码关注云+社区

领取腾讯云代金券