当前位置: 首页 > news >正文

设计模式之命令模式详解(Command Pattern)

在软件开发系统中,常常出现“方法的请求者”与“方法的实现者”之间存在紧密的耦合关系。这不利于软件功能的扩展与维护。例如,想对行为进行“撤销、重做、记录”等处理都很不方便,因此“如何将方法的请求者与方法的实现者解耦?”变得很重要,命令模式能很好地解决这个问题。

在现实生活中,这样的例子也很多,例如,电视机遥控器(命令发送者)通过按钮(具体命令)来遥控电视机(命令接收者),还有计算机键盘上的“功能键”等。

命令模式的定义与特点

命令(Command)模式的定义如下:将一个请求封装为一个对象,使发出请求的责任和执行请求的责任分割开。这样两者之间通过命令对象进行沟通,这样方便将命令对象进行储存、传递、调用、增加与管理。

命令模式的优点

  1. 将发起请求的对象与执行请求的对象解耦。
  2. 容易实现对请求的撤销和重做
  3. 增加或删除命令非常方便。采用命令模式增加与删除命令不会影响其他类,它满足“开闭原则”,对扩展比较灵活。
  4. 容易设计一个命令队列。只要把命令对象放到列队,就可以多线程的执行命令

缺点:可能产生大量具体命令类。因为计对每一个具体操作都需要设计一个具体命令类,这将增加系统的复杂性。

命令模式的原理类图与角色说明

命令模式的原理类图

角色说明

  1. Invoker: 是请求的发送者,它通常拥有很多的命令对象,并通过访问命令对象来执行相关请求,它不直接访问接收者。
  2. Command: 声明执行命令的接口,拥有执行命令的抽象方法 execute()。
  3. Receiver: 实现者/接受者角色,执行命令功能的相关操作,是具体命令对象业务的真正实现者。
  4. ConcreteCommand: 是抽象命令类的具体实现类,将一个接受者对象与一个动作绑定,调用接受者相应的操作,实现 execute。

命令模式的具体实现

以遥控器为实例运用命令模式

Command(执行命令的接口)
//创建命令接口
public interface Command {
    //执行动作(操作)
    public void execute();
    //撤销动作(操作)
    public void undo();
}
ConcreteCommand(抽象命令类的具体实现类)
//开灯命令
public class LightOffCommand implements Command {
    // 聚 合 LightReceiver
    LightReceiver light;
    // 构造器
    public LightOffCommand(LightReceiver light) { super();
        this.light = light;
    }

    @Override
    public void execute() {
        // 调用接收者的方法
        light.off();
    }

    @Override
    public void undo() {
        // 调用接收者的方法
        light.on();
    }
}
//关灯命令
public class LightOnCommand implements Command {
    // 聚 合 LightReceiver
    LightReceiver light;
    // 构造器
    public LightOnCommand(LightReceiver light) { super();
        this.light = light;
    }

    @Override
    public void execute() {
        // 调用接收者的方法
        light.on();
    }

    @Override
    public void undo() {
        // 调用接收者的方法
        light.off();
    }
}
/**
 * 	没有任何命令,即空执行: 用于初始化每个按钮, 当调用空命令时,对象什么都不做
 * 	其实,这样是一种设计模式, 可以省掉对空判断
 */
public class NoCommand implements Command {
    @Override
    public void execute() {

    }

    @Override
    public void undo() {

    }
}
Receiver(实现者/接受者)
//开关灯的具体实现
public class LightReceiver {
    public void on() {
        System.out.println(" 电灯打开了.. ");
    }

    public void off() {
        System.out.println(" 电灯关闭了.. ");
    }
}
Invoker(请求的发送者)
//遥控器类
public class RemoteController {
    // 开 按钮的命令数组
    Command[] onCommands;
    Command[] offCommands;

    // 执行撤销的命令
    Command undoCommand;

    // 构造器,完成对按钮初始化
    public RemoteController(){
        onCommands = new Command[5];
        offCommands = new Command[5];

        for (int i = 0; i < 5; i++) {
            onCommands[i] = new NoCommand();
            offCommands[i] = new NoCommand();
        }
    }

    // 给我们的按钮设置你需要的命令
    public void setCommand(int no, Command onCommand, Command offCommand) {
        onCommands[no] = onCommand;
        offCommands[no] = offCommand;
    }

    // 按下开按钮
    public void onButtonWasPushed(int no) { // no 0
        // 找到你按下的开的按钮, 并调用对应方法
        onCommands[no].execute();
        // 记录这次的操作,用于撤销
        undoCommand = onCommands[no];
    }

    // 按下开按钮
    public void offButtonWasPushed(int no) { // no 0
        // 找到你按下的关的按钮, 并调用对应方法
        offCommands[no].execute();
        // 记录这次的操作,用于撤销
        undoCommand = offCommands[no];
    }

    // 按下撤销按钮
    public void undoButtonWasPushed() {
        undoCommand.undo();
    }
}
客户端调用
public class Client {
    public static void main(String[] args) {
        //使用命令设计模式,完成通过遥控器,对电灯的操作

        //创建电灯的对象(接受者)
        LightReceiver lightReceiver = new LightReceiver();

        //创建电灯相关的开关命令
        LightOnCommand lightOnCommand = new LightOnCommand(lightReceiver);
        LightOffCommand lightOffCommand = new LightOffCommand(lightReceiver);

        //需要一个遥控器
        RemoteController remoteController = new RemoteController();

        //给我们的遥控器设置命令, 比如 no = 0  是电灯的开和关的操作
        remoteController.setCommand(0, lightOnCommand, lightOffCommand);

        System.out.println("--------按下灯的开按钮-----------");
        remoteController.onButtonWasPushed(0);
        System.out.println("--------按下灯的关按钮-----------");
        remoteController.offButtonWasPushed(0);
        System.out.println("--------按下撤销按钮-----------");
        remoteController.undoButtonWasPushed();
    }
}
/*
输出结果:
--------按下灯的开按钮-----------
 电灯打开了.. 
--------按下灯的关按钮-----------
 电灯关闭了.. 
--------按下撤销按钮-----------
 电灯打开了.. 
*/

注:
上述的NoCommand类是一个空命令,空命令也是一种设计模式,它为我们省去了判空的操作。在上面的实例中,如果没有用空命令,我们每按下一个按键都要判空,这给我们编码带来一定的麻烦。

相关文章:

  • 今天试用了VE开发SWT
  • 设计模式之访问者模式详解(Visitor Pattern)
  • GCC for Win32开发环境介绍(2)
  • 设计模式之观察者模式详解(Observer Pattern)
  • 设计模式之迭代器模式详解(Iterator Pattern)
  • asp.net部分优化
  • 设计模式之中介者模式详解(Mediator Pattern)
  • ASP.NET中利用存储过程实现模糊查询
  • 设计模式之备忘录模式详解(Memento Pattern)
  • 从asp转asp.net的相关
  • 设计模式之状态模式详解(State Pattern)
  • 浅谈DataSet 的用法
  • 多维数组和矩阵之顺时针打印二维数组
  • 各种数据库连接代码(JSP)
  • 多维数组与矩阵之0所在的行列清零
  • 分享的文章《人生如棋》
  • 230. Kth Smallest Element in a BST
  • Akka系列(七):Actor持久化之Akka persistence
  • Babel配置的不完全指南
  • CAP理论的例子讲解
  • echarts的各种常用效果展示
  • HTML-表单
  • IndexedDB
  • JavaScript 奇技淫巧
  • Javascript编码规范
  • JavaScript中的对象个人分享
  • php的插入排序,通过双层for循环
  • PHP的类修饰符与访问修饰符
  • React-redux的原理以及使用
  • 阿里中间件开源组件:Sentinel 0.2.0正式发布
  • 从0搭建SpringBoot的HelloWorld -- Java版本
  • 当SetTimeout遇到了字符串
  • 基于组件的设计工作流与界面抽象
  • 算法-插入排序
  • 算法之不定期更新(一)(2018-04-12)
  • 通过来模仿稀土掘金个人页面的布局来学习使用CoordinatorLayout
  • 走向全栈之MongoDB的使用
  • 正则表达式-基础知识Review
  • #define 用法
  • #pragma once与条件编译
  • $(function(){})与(function($){....})(jQuery)的区别
  • (14)目标检测_SSD训练代码基于pytorch搭建代码
  • (3)(3.2) MAVLink2数据包签名(安全)
  • (八)光盘的挂载与解挂、挂载CentOS镜像、rpm安装软件详细学习笔记
  • (附源码)计算机毕业设计SSM教师教学质量评价系统
  • (学习日记)2024.03.12:UCOSIII第十四节:时基列表
  • (一)python发送HTTP 请求的两种方式(get和post )
  • (原創) 未来三学期想要修的课 (日記)
  • (转)LINQ之路
  • .360、.halo勒索病毒的最新威胁:如何恢复您的数据?
  • .net core Swagger 过滤部分Api
  • .Net IE10 _doPostBack 未定义
  • .net oracle 连接超时_Mysql连接数据库异常汇总【必收藏】
  • .NET 除了用 Task 之外,如何自己写一个可以 await 的对象?
  • .NET 应用架构指导 V2 学习笔记(一) 软件架构的关键原则