基本需求:

  • 一套智能家电,有照明灯、风扇、冰箱、洗衣机,我们只要在手机上安装app就可以控制对这些家电工作
  • 这些智能家电来自不同的厂家,我们不想针对每一种家电都安装一个App分别控制,我们希望只要一个app就可以控制全部智能家电
  • 要实现一个app控制所有智能家电的需要,则每个智能家电厂家都要提供一个统一的接口给app调用,这时就可以考虑使用命令模式
  • 命令模式可将“动作的请求者”从“动作的执行者”对象中解耦出来
  • 也就是说,每一种家电都有开和关两种操作,一组命令

基本介绍:

  • 命令模式(Command):在软件设计中,我们经常需要向某些对象发送请求,并不知道请求的接收者是谁,不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计

  • 命令模式使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦

  • 命令模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求(即命令),同时命令模式也必须支持可撤销的操作

  • 将军发布命令,士兵去执行。其中有几个角色:将军(命令发布者)、士兵(命令的具体执行者)、命令(连接将军和士兵)

    • Invoker 是调用者(将军),Receiver 是被调用者(士兵),MyCommand 是命令,实现了 Command 接口,持有接收对象
  • UML类图(原理)

    • 说明
      • Invoker:命令调用者角色,聚合命令
      • Command:是命令接口
      • ConcreteCommand:命令的具体实现,聚合命令的接受者
      • Receiver:命令的接受者(执行者)
  • UML类图(案例)

  • 代码实现

    • public class LightReceiver {
      
         // 电灯命令的执行者
      
         public void on() {
      System.out.println("电灯打开了");
      } public void off() {
      System.out.println("电灯关闭了");
      } }
    • public interface Command {
      
         // 命令接口
      
         // 执行命令和撤销命令
      void execute(); void undo(); } // 子类一 电灯开命令
      class LightOnCommand implements Command{ // 聚合命令的执行者
      private LightReceiver lightReceiver; public LightOnCommand(LightReceiver lightReceiver) {
      this.lightReceiver = lightReceiver;
      } @Override
      public void execute() {
      lightReceiver.on();
      } @Override
      public void undo() {
      lightReceiver.off();
      } } // 子类二 电灯关命令
      class LightOffCommand implements Command{ // 聚合命令的执行者
      private LightReceiver lightReceiver; public LightOffCommand(LightReceiver lightReceiver) {
      this.lightReceiver = lightReceiver;
      } @Override
      public void execute() {
      lightReceiver.off();
      } @Override
      public void undo() {
      lightReceiver.on();
      } } // 子类三 空命令 对命令接口空实现
      class NoCommand implements Command { @Override
      public void execute() { } @Override
      public void undo() { } }
    • // 命令调用者
      public class RemoteController { // 开命令的集合
      private Command[] onCommands; // 关命令的集合
      private Command[] offCommands; // 记录上一个执行的命令,便于进行撤销
      private Command undoCommand; public RemoteController() {
      // 默认初始化五组开关 一组开关有开和关两个按钮,一一对应
      this.onCommands = new Command[5];
      this.offCommands = new Command[5];
      for (int i = 0; i < 5; i++) {
      // 将开关命令的值初始化成空命令,防止空指针异常
      this.onCommands[i] = new NoCommand();
      this.offCommands[i] = new NoCommand();
      }
      this.undoCommand = new NoCommand();
      } // 为某组开关设置命令
      public void setCommand(int index, Command onCommand, Command offCommand) {
      if (index >= 0 && index < this.onCommands.length) {
      this.onCommands[index] = onCommand;
      this.offCommands[index] = offCommand;
      }
      } // 按下开的按钮,发送命令执行
      public void onButtonWasPushed(int index) {
      if (index >= 0 && index < this.onCommands.length) {
      this.onCommands[index].execute();
      // 设置撤销命令
      this.undoCommand = this.onCommands[index];
      }
      } // 按下关的按钮,发送命令执行
      public void offButtonWasPushed(int index) {
      if (index >= 0 && index < this.onCommands.length) {
      this.offCommands[index].execute();
      // 设置撤销命令
      this.undoCommand = this.offCommands[index];
      }
      } // 按下撤销按钮,发送命令执行,只支持撤销一次
      public void undoButtonWasPushed() {
      this.undoCommand.undo();
      // 重置撤销命令
      this.undoCommand = new NoCommand();
      } }
    • public class Client {
      public static void main(String[] args) {
      // 创建执行者
      LightReceiver lightReceiver = new LightReceiver();
      // 创建一组电灯开关的命令,并设置执行者
      Command lightOnCommand = new LightOnCommand(lightReceiver);
      Command lightOffCommand = new LightOffCommand(lightReceiver);
      // 创建命令的发送者,并设置电灯这一组命令
      RemoteController remoteController = new RemoteController();
      remoteController.setCommand(0, lightOnCommand, lightOffCommand);
      // 发送电灯命令 执行
      System.out.println("------发送电灯开命令------");
      remoteController.onButtonWasPushed(0);
      System.out.println("------发送电灯关命令------");
      remoteController.offButtonWasPushed(0);
      System.out.println("------发送撤销命令------");
      remoteController.undoButtonWasPushed(); // 使用命令模式对命令进行了封装,将命令的发布者和执行者进行了松耦合,利于系统的扩展
      // 比如再有一组电视的命令,直接创建新的命令类,创建其对象将其设置给命令的发布者即可,原有的代码不需要改变
      }
      }

spring源码:

  • 在spring的JdbcTemplate类中就使用到了命令模式

  • // 在JdbcTemplate的query和execute方法中有如下代码
    public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException {
    ......
    // StatementCallback是一个接口,只有一个doInStatement方法,相当于命令接口
    // QueryStatementCallback局部内部类 实现了StatementCallback,相当于具体的命令的,另外,在此处还充当了命令的执行者
    // StatementCallback接口共有四个实现类,均在JdbcTemplate某个方法内部,作为局部内部类
    // BatchUpdateStatementCallback、UpdateStatementCallback、QueryStatementCallback、ExecuteStatementCallback
    class QueryStatementCallback implements StatementCallback<T>, SqlProvider {
    ......
    public T doInStatement(Statement stmt) throws SQLException { }
    ......
    }
    // 调用execute方法
    return this.execute((StatementCallback)(new QueryStatementCallback()));
    } // 在execute方法内部调用了命令接口中的方法,而execute方法又属于JdbcTemplate,即JdbcTemplate为命令的调用者
    public <T> T execute(StatementCallback<T> action) throws DataAccessException {
    ......
    T result = action.doInStatement(stmt);
    ......
    }

注意事项:

  • 主要解决:在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适

  • 命令模式是一种数据驱动的设计模式,属于行为型模式,请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令

  • 将发起请求的对象与执行请求的对象解耦。发起请求的对象是调用者,调用者只要调用命令对象的 execute()方法就可以让接收者工作,而不必知道具体的接收者对象是谁、是如何实现的,命令对象会负责让接收者执行请求的动作,也就是说:”请求发起者”和“请求执行者”之间的解耦是通过命令对象实现的,命令对象起到了纽带桥梁的作用

  • 容易设计一个命令队列,只要把命令对象放到列队,就可以多线程的执行命令

  • 容易实现对请求的撤销和重做

  • 命令模式不足:可能导致某些系统有过多的具体命令类,增加了系统的复杂度,这点在在使用的时候要注意

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

  • 命令模式经典的应用场景:界面的一个按钮都是一条命令、模拟 CMD(DOS 命令)订单的撤销/恢复、触发-反馈机制

14.java设计模式之命令模式的更多相关文章

  1. 折腾Java设计模式之命令模式

    博客原文地址 折腾Java设计模式之命令模式 命令模式 wiki上的描述 Encapsulate a request as an object, thereby allowing for the pa ...

  2. Java设计模式 之 命令模式

    1      从属模式分类 行为性模式 2      命令模式意图 命令模式可将动作的请求者和动作的执行者对象中解耦. 该模式将一个行为操作发起者的请求封装到对象中,该请求由另外一个对象执行. 将动作 ...

  3. JAVA设计模式之 命令模式【Command Pattern】

    一.概述 命令模式能够将请求发送者和接收者全然解耦.发送者与接收者之间没有直接引用关系,发送请求的对象仅仅须要知道怎样发送请求,而不必知道怎样完毕请求.核心在于引入了命令类,通过命令类来减少发送者和接 ...

  4. java设计模式之命令模式

    学校中.生活中.社会中总是会存在一定的阶层,虽然我们很多人都不可认可阶层的存在.命令这一词也就在阶层中诞生.家长命令孩子,老师命令学生,领导命令小娄娄.这些都在我们的生活存在的东西,相信这一个模式学习 ...

  5. java设计模式之命令模式以及在java中作用

    命令模式属于对象的行为模式.命令模式又称为行动(Action)模式或交易(Transaction)模式. 命令模式把一个请求或者操作封装到一个对象中.命令模式允许系统使用不同的请求把客户端参数化,对请 ...

  6. 玉帝传美猴王上天,大闹天宫之Java设计模式:命令模式

    目录 示例 改进代码 命令模式 定义 意图 主要解决问题 何时使用 优缺点 玉帝传美猴王上天 命令模式和策略模式的区别 示例 系统需要设计一个命令行界面,用户可输入命令来执行某项功能,系统的功能会不断 ...

  7. 用Java 8 Lambda表达式实现设计模式:命令模式

    在这篇博客里,我将说明如何在使用 Java 8 Lambda表达式 的函数式编程方式 时实现 命令 设计模式 .命令模式的目标是将请求封装成一个对象,从对客户端的不同类型请求,例如队列或日志请求参数化 ...

  8. 折腾Java设计模式之状态模式

    原文地址 折腾Java设计模式之状态模式 状态模式 在状态模式(State Pattern)中,类的行为是基于它的状态改变的.这种类型的设计模式属于行为型模式.在状态模式中,我们创建表示各种状态的对象 ...

  9. java设计模式3--单例模式(Singleton)

    本文地址:http://www.cnblogs.com/archimedes/p/java-singleton-pattern.html,转载请注明源地址. 单例模式 保证一个类仅有一个实例,并提供一 ...

随机推荐

  1. drf 视图使用及源码分析

    前言 drf视图的源码非常的绕,但是实现的功能却非常的神奇. 它能够帮你快速的解决ORM增删改查的重复代码,非常的方便好用. 下面是它源码中的一句话: class ViewSetMixin: &quo ...

  2. ceph踩坑日记之rgw_dynamic_resharding

    1.背景说明 参考说明: https://access.redhat.com/documentation/en-us/red_hat_ceph_storage/2/html/object_gatewa ...

  3. 【面经】面试官:如何以最高的效率从MySQL中随机查询一条记录?

    写在前面 MySQL数据库在互联网行业使用的比较多,有些小伙伴可能会认为MySQL数据库比较小,存储不了很多的数据.其实,这些小伙伴是真的不了解MySQL.MySQL的小不是说使用MySQL存储的数据 ...

  4. 2018noip游记

    2018noip游记 相隔一年多才想起可以弄一篇博客纪念一下我的首次比赛, 以现在的水平回望过去,发现很好玩很有纪念意义, 于是这篇博客诞生了 \(T1\) 当时的我刚学会什么是字符串,但仍然很不熟练 ...

  5. 转载:解密Redis持久化

    本文内容来源于Redis作者博文,Redis作者说,他看到的所有针对Redis的讨论中,对Redis持久化的误解是最大的,于是他写了一篇长文来对Redis的持久化进行了系统性的论述.文章非常长,也很值 ...

  6. dubbo2.7.X版本带来的服务注册和服务调用方式改变

    参考地址:https://www.cnblogs.com/alisystemsoftware/p/13064620.html 注册中心数据结构格式改变(service:接口服务,application ...

  7. 分布式文档存储数据库之MongoDB索引管理

    前文我们聊到了MongoDB的简介.安装和对collection的CRUD操作,回顾请参考https://www.cnblogs.com/qiuhom-1874/p/13941797.html:今天我 ...

  8. Ubuntu 16.04升级18.04

    原文:https://blog.csdn.net/sean_8180/article/details/81075659 1.更新资源$ sudo apt-get update$ sudo apt-ge ...

  9. pc端兼容IE9及以上版本

    最近业务部门反映我们商城的兼容性不是很好,尤其是在IE浏览器上,经过调研,我们决定对IE9及以上版本的IE内核浏览器进行主流程测试,发现有哪些功能在IE9上不兼容 一.CSS兼容性 如下图所示 使用了 ...

  10. 1、线性DP 354. 俄罗斯套娃信封问题

    354. 俄罗斯套娃信封问题 https://leetcode-cn.com/problems/russian-doll-envelopes/ 算法分析 首先我们从两种情况来讨论这个问题: w无重复值 ...