文章目录

  • 1 命令模式介绍
  • 2 命令模式详解
    • 2.1 命令模式结构
    • 2.2 命令模式实现
    • 2.3 命令模式应用举例
  • 3 实现命令队列

1 命令模式介绍

在现实生活中人们通过使用开关来控制一些电器的打开和关闭,例如电灯或者排气扇,如下图。

我们可以将开关看成一个请求发送者,电灯或者排气扇则是请求的最终接收者和处理者。开关和电灯之间并不存在直接耦合关系,它们通过电线连接在一起,使用不同的电线可以连接不同的请求接收者,只需要更换一根电线,相同的发送者(开关)即可对应不同的接收者(电器)。

在软件开发中也存在很多与开关和电器类似的请求发送者和接收者对象,例如按钮和事件处理类。为了降低系统的耦合度,将请求的发送者和接收者解耦,我们可以使用命令模式(Command Pattern)来设计系统。

在命令模式中发送者与接收者之间引入了新的命令对象(类似电线),将发送者的请求封装在命令对象中,再通过命令对象来调用接收者的方法。它可以使请求发送者和接收者完全解耦,发送者和接收者之间没有直接引用的关系,发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求。

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

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

何时使用: 在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。

如何解决: 通过调用者调用接受者执行命令,顺序:调用者→命令→接受者。

关键代码: 定义三个角色:1、received 真正的命令执行对象 2、Command 3、invoker 使用命令对象的入口

应用实例: struts 1 中的 action 核心控制器 ActionServlet 只有一个,相当于 Invoker,而模型层的类会随着不同的应用有不同的模型类,相当于具体的 Command。

优点: 1、降低了系统耦合度。 2、新的命令可以很容易添加到系统中去。

缺点: 使用命令模式可能会导致某些系统有过多的具体命令类。

使用场景: 认为是命令的地方都可以使用命令模式,比如: 1、GUI 中每一个按钮都是一条命令。 2、模拟 CMD。

注意事项: 系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作,也可以考虑使用命令模式,见命令模式的扩展。

2 命令模式详解

2.1 命令模式结构

命令模式的核心在于引入了抽象命令类和具体命令类,通过命令类来降低发送者和接收者的耦合度,请求发送者只需指定一个命令对象,再通过命令对象来调用请求接收者的处理方法,结构图如下:

由图可知,命令模式包含以下4个角色。

  1. Command(抽象命令类):抽象命令类一般是一个抽象类或接口,在其中声明了用于执行请求的execute()等方法,通过这些方法可以调用请求接收者的相关操作。
  2. ConcreteCommand(具体命令类):具体命令类是抽象命令类的子类,实现了在抽象命令类中声明的方法,它对应具体的接收者对象,将接收者对象的动作绑定其中。具体命令类在实现execute()方法时将调用接收者对象的相关操作(Action)。
  3. Invoker(调用者):调用者即请求发送者,它通过命令对象来执行请求。一个调用者并不需要在设计时确定其接收者,因此它只与抽象命令类之间存在关联关系。在程序运行时可以将一个具体命令对象注入其中,再调用具体命令对象的execute()方法,从而实现间接调用请求接收者的相关操作。
  4. Receiver(接收者):接收者执行与请求相关的操作,具体实现对请求的业务处理。

2.2 命令模式实现

典型的抽象命令类代码如下:

public abstract class Command {public abstract void execute();
}

对于请求发送者(即调用者)而言,将针对抽象命令类进行编程,可以通过构造函数或者Setter方法在运行时注入具体命令类对象,并在业务方法中调用命令对象的execute()方法,其典型代码如下:

public class Invoker {private Command command;// 构造注入public Invoker(Command command) {this.command = command;}// 设值注入public setCommand(Command command) {this.command = command;}// 业务方法,用于调用命令类中的execute()方法public void call() {command.execute();}
}

具体命令类继承了抽象命令类,它与请求接收者关联,实现了在抽象命令类中声明的execute()方法,并在实现时调用接收者的请求响应方法。其典型代码如下:

public class ConcreteCommand extends Command {private Receiver receiver; //维持一个对请求接收者对象的引用public void execute() {receiver.action(); //调用请求接收者的业务处理方法action()}
}

请求接收者Receiver具体实现对请求的业务处理,它拥有action()方法,用于执行与请求相关操作,其典型代码如下:

public class Receiver {public void action() {//具体操作}
}

2.3 命令模式应用举例

  • 题目描述

    为了用户使用方便,某系统提供了一系列功能键,用户可以自定义功能键的功能,例如功能键FunctionButton可以用于退出系统(由SystemExitClass类来实现),也可以用于显示帮助文档(由DisplayHelpClass类来实现)。

    用户可以通过修改配置文件来改变功能键的用途,现使用命令模式来设计该系统,使得功能键类与功能类之间解耦,可为同一个功能键设置不同的功能。

  • UML类图

其中,FunctionButton充当请求调用者,SystemExitClass和DisplayHelpClass充当请求接收者,Command是抽象命令类,ExitCommand和HelpCommand充当具体命令类。

  • 代码

    代码地址

3 实现命令队列

有时候,当一个请求发送者发送一个请求时有不止一个请求接收者产生响应,这些请求接收者将逐个执行业务方法,完成对请求的处理,此时可以通过命令队列来实现。

命令队列的实现方法有多种形式,其中最常用、灵活性最好的一种方式就是增加一个CommandQueue类,由该类负责存储多个命令对象,而不同的命令对象可以对应不同的请求接收者。CommandQueue类的典型代码如下:

package homework;import java.util.ArrayList;
import java.util.List;public class CommandQueue {private List<Command> commandList = new ArrayList();public void addCommand(Command command) {commandList.add(command);}public void removeCommand(Command command) {commandList.remove(command);}/*** 循环调用每一个命令对象的execute()方法*/public void execute() {for (Command command : commandList) {command.execute();}}
}

在增加命令队列类CommandQueue以后,请求发送者Invoker将针对CommandQueue编程。即将Command修改为CommandQueue即可。

设计模式之命令模式详解(附应用举例实现)相关推荐

  1. 设计模式之命令模式详解

    1 概述 日常生活中,我们出去吃饭都会遇到下面的场景.我们可以将女招待理解成一个请求的发送者,用户通过它来发送一个"点餐"请求,而厨师是"点餐"请求的最终接收者 ...

  2. 设计模式之命令模式详解(故事版)

    作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 背景:小左是魔都某公司技术部 ...

  3. (十二)命令模式详解(故事版)- 转

    作者:zuoxiaolong8810(左潇龙),转载请注明出处. 背景:小左是魔都某公司技术部的一名屌丝程序猿,每天的工作就是维护一个20世纪的古董级项目,由于公司不大,所以公司很多制度不太完善,导致 ...

  4. 设计模式之模板方法模式详解

    设计模式之模板方法模式详解 概述 在面向对象程序设计过程中,程序员常常会遇到这种情况:设计一个系统时知道了算法所需的关键步骤,而且确定了这些步骤的执行顺序,但某些步骤的具体实现还未知,或者说某些步骤的 ...

  5. 设计模式之门面模式详解

    设计模式之门面模式详解 文章目录 设计模式之门面模式详解 一.什么是门面模式 二.门面模式的应用场景 三.门面模式的角色组成 四.门面模式通用写法 五.门面模式在业务中的应用 六.门面模式优缺点 一. ...

  6. 设计模式——模版方法模式详解(论沉迷LOL对学生的危害)

    0. 前言 写在最前面,本人的设计模式类博文,建议先看博文前半部分的理论介绍,再看后半部分的实例分析,最后再返回来复习一遍理论介绍,这时候你就会发现我在重点处标红的用心,对于帮助你理解设计模式有奇效哦 ...

  7. 设计模式之桥接模式详解

    设计模式之桥接模式详解 文章目录 设计模式之桥接模式详解 一.什么是桥接模式 二.桥接模式的应用场景 三.桥接模式的角色组成 四.桥接模式通用写法示例 五.桥接模式优缺点 一.什么是桥接模式 桥接模式 ...

  8. 设计模式之策略模式详解

    设计模式之策略模式详解 概述 先看下面的图片,我们去旅游选择出行模式有很多种,可以骑自行车.可以坐汽车.可以坐火车.可以坐飞机. 作为一个程序猿,开发需要选择一款开发工具,当然可以进行代码开发的工具有 ...

  9. 设计模式之工厂模式详解(附应用举例实现)

    文章目录 1 工厂模式介绍 2 工厂模式详解 2.1 简单工厂模式 2.1.1 简单工厂模式结构 2.1.2 简单工厂模式实现 2.1.3 简单工厂模式应用举例 2.2 工厂方法模式 2.2.1 工厂 ...

最新文章

  1. jquery 点击计数器
  2. java win8 mac地址_Win8怎么查看MAC地址_Win8查看电脑MAC地址方法-192路由网
  3. 下拉列表左右选择案例
  4. android 清空所有控件,如何清空android ListView控件的内容
  5. 论文的写作要求、流程与写作技巧
  6. nuxt.js的核心代码_Nuxt.js中的通用应用程序代码结构
  7. RMAN备份与还原 - 参考案例
  8. 远程通信(RPC,Webservice,RMI,JMS、EJB、JNDI的区别)对比
  9. ASP.NET的自定义分页
  10. np.dot和np.matmul的区别与联系
  11. 15分钟搞定OLAP查询引擎Phoenix
  12. 产品介绍丨世炬5G一体化基站
  13. FlashFXP连接linux服务器(centos7环境)提示连接失败 (Unable to access SFTP sub-system, operation failed.)
  14. 大数据是什么?初学者怎样理解大数据技术
  15. lambda表达式的3种写法
  16. 微服务启动成功无法注册到服务注册中心
  17. Cesium中自定义材质material
  18. 如果你刚刚入门数据可视化,那这些你千万不能错过!
  19. 计算机论文一千五,研究生为一千五奖学金花七八千找期刊登论文
  20. MCGS嵌入软件配置教程

热门文章

  1. python训练词库_jieba 分词库(python)
  2. STM32之ADC(模拟量-数字量的转化)
  3. 使用 Mac Terminal 终端查看系统进程占用的CPU内存等信息
  4. Adobe Photoshop CC 2020中文版
  5. 又是为了触屏移动设备而设计
  6. PHP二维数组排序算法函数
  7. 浏览器安装查看UE图的插件(Axure RP)
  8. 日常DDoS防御的一些小知识
  9. Istio 正式成为 CNCF 孵化项目,F-16 战斗机早部署上了?
  10. Notepad++插件安装