22、Command 命令模式
1、command 命令模式
命令模式(Command Pattern):在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计 命名模式使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活,实现解耦。
在命名模式中,会将一个请求封装为一个对象,以便使用不同参数来表示不同的请求(即命名),同时命令模式也支持可撤销的操作。
通俗易懂的理解:将军发布命令,士兵去执行。其中有几个角色:将军(命令发布者)、士兵(命令的具体执行者)、命令(连接将军和士兵)。
意图:将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。
主要解决:在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。
何时使用:在某些场合,比如要对行为进行"记录、撤销/重做、事务"等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将"行为请求者"与"行为实现者"解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。
2、示例代码
2.1 撤销操作的实现
一个简易计算器,该计算器可以实现简单的数学运算,还可以对运算实施撤销操作。
//加法类:请求接收者
class Adder {
private int num=0; //定义初始值为0
//加法操作,每次将传入的值与num作加法运算,再将结果返回
public int add(int value) {
num += value;
return num;
}
}
//抽象命令类
abstract class AbstractCommand {
public abstract int execute(int value); //声明命令执行方法execute()
public abstract int undo(); //声明撤销方法undo()
}
//具体命令类
class ConcreteCommand extends AbstractCommand {
private Adder adder = new Adder();
private int value;
//实现抽象命令类中声明的execute()方法,调用加法类的加法操作
public int execute(int value) {
this.value=value;
return adder.add(value);
} //实现抽象命令类中声明的undo()方法,通过加一个相反数来实现加法的逆向操作
public int undo() {
return adder.add(-value);
}
} //计算器界面类:请求发送者
class CalculatorForm {
private AbstractCommand command; public void setCommand(AbstractCommand command) {
this.command = command;
} //调用命令对象的execute()方法执行运算
public void compute(int value) {
int i = command.execute(value);
System.out.println("执行运算,运算结果为:" + i);
} //调用命令对象的undo()方法执行撤销
public void undo() {
int i = command.undo();
System.out.println("执行撤销,运算结果为:" + i);
}
}
编写如下客户端测试代码:
class Client {
public static void main(String args[]) {
CalculatorForm form = new CalculatorForm();
AbstractCommand command;
command = new ConcreteCommand();
form.setCommand(command); //向发送者注入命令对象
form.compute(10);
form.compute(5);
form.compute(10);
form.undo();
}
}
编译并运行程序,输出结果如下:
执行运算,运算结果为:10
执行运算,运算结果为:15
执行运算,运算结果为:25
执行撤销,运算结果为:15
2.2 多命令对列的实现
有时候我们需要将多个请求排队,当一个请求发送者发送一个请求时,将不止一个请求接收者产生响应,这些请求接收者将逐个执行业务方法,完成对请求的处理。此时,我们可以通过命令队列来实现。
命令队列的实现方法有多种形式,其中最常用、灵活性最好的一种方式是增加一个CommandQueue类,由该类来负责存储多个命令对象,而不同的命令对象可以对应不同的请求接收者,CommandQueue类的典型代码如下所示:
import java.util.*;
class CommandQueue {
//定义一个ArrayList来存储命令队列
private ArrayList<Command> commands = new ArrayList<Command>();
public void addCommand(Command command) {
commands.add(command);
}
public void removeCommand(Command command) {
commands.remove(command);
}
//循环调用每一个命令对象的execute()方法
public void execute() {
for (Object command : commands) {
((Command)command).execute();
}
}
}
在增加了命令队列类CommandQueue以后,请求发送者类Invoker将针对CommandQueue编程,代码修改如下:
class Invoker {
private CommandQueue commandQueue; //维持一个CommandQueue对象的引用
//构造注入
public Invoker(CommandQueue commandQueue) {
this. commandQueue = commandQueue;
}
//设值注入
public void setCommandQueue(CommandQueue commandQueue) {
this.commandQueue = commandQueue;
}
//调用CommandQueue类的execute()方法
public void call() {
commandQueue.execute();
}
}
命令队列与我们常说的“批处理”有点类似。批处理,顾名思义,可以对一组对象(命令)进行批量处理,当一个发送者发送请求后,将有一系列接收者对请求作出响应,命令队列可以用于设计批处理应用程序,如果请求接收者的接收次序没有严格的先后次序,我们还可以使用多线程技术来并发调用命令对象的execute()方法,从而提高程序的执行效率。
3、command 模式类图
在命令模式结构图中包含如下几个角色:
● Command(抽象命令类):抽象命令类一般是一个抽象类或接口,在其中声明了用于执行请求的execute()等方法,通过这些方法可以调用请求接收者的相关操作。
● ConcreteCommand(具体命令类):具体命令类是抽象命令类的子类,实现了在抽象命令类中声明的方法,它对应具体的接收者对象,将接收者对象的动作绑定其中。在实现execute()方法时,将调用接收者对象的相关操作(Action)。
● Invoker(调用者):调用者即请求发送者,它通过命令对象来执行请求。一个调用者并不需要在设计时确定其接收者,因此它只与抽象命令类之间存在关联关系。在程序运行时可以将一个具体命令对象注入其中,再调用具体命令对象的execute()方法,从而实现间接调用请求接收者的相关操作。
● Receiver(接收者):接收者执行与请求相关的操作,它具体实现对请求的业务处理。
命令模式的本质是对请求进行封装,一个请求对应于一个命令,将发出命令的责任和执行命令的责任分割开。每一个命令都是一个操作:请求的一方发出请求要求执行一个操作;接收的一方收到请求,并执行相应的操作。命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求如何被接收、操作是否被执行、何时被执行,以及是怎么被执行的。
4、小结
1、命令模式就是将一段操作逻辑或方法封装到一个命令对象中,命令对象可以作为队列、日志等的参数进行传递,实现命令的调用者和具体命令的实现解耦。
2、其实命令模式和策略模式以及工厂模式都有一些相似之处,但是不同点主要在于他们的使用场景不同,策略模式主要是组合一族算法簇,实现功能的扩展;命令模式用于将具体的命令封装成对象,就可以传递和存储,灵活的被调用执行,可以被记录在日志中用于系统恢复,也可以用在队列中,排队执行,它还支持撤销功能;而工厂模式则用于创建对象。其实很多设计模式他们在代码实现和一些思路上确实有一些相似之处,因为他们都是充分的应用了java语言的特性(封装、继承、多态、抽象等),用于解决特定类型问题的模式,因此我们应该更关心他们的使用场景,细心体会那一丝略微的不同和巧妙之处。
3、任何模式都不能脱离具体的使用场景随意使用,有时要结合实际情况选择合适的设计模式,甚至有时无模式胜过有模式,代码简洁同样重要。
优点: 1、降低了系统耦合度。2、新的命令可以很容易添加到系统中去。
缺点:使用命令模式可能会导致某些系统有过多的具体命令类。
使用场景:认为是命令的地方都可以使用命令模式,比如:1、GUI 中每一个按钮都是一条命令。2、模拟 CMD。
公众号:发哥讲
这是一个稍偏基础和偏技术的公众号,甚至其中包括一些可能阅读量很低的包含代码的技术文,不知道你是不是喜欢,期待你的关注。
如果你觉得文章还不错,就请点击右上角选择发送给朋友或者转发到朋友圈~
● 扫码关注我们
据说看到好文章不推荐的人,服务器容易宕机!
本文版权归发哥讲和博客园共有,原创文章,未经允许不得转载,否则保留追究法律责任的权利。
22、Command 命令模式的更多相关文章
- Java设计模式(22)命令模式(Command模式)
Command模式是最让我疑惑的一个模式,我在阅读了很多代码后,才感觉隐约掌握其大概原理,我认为理解设计模式最主要是掌握起原理构造,这样才对自己实际编程有指导作用.Command模式实际上不是个很具体 ...
- C++设计模式-Command命令模式
Command命令模式作用:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化:对请求排队或记录请求日志,以及支持可撤销的操作. 由于“行为请求者”与“行为实现者”的紧耦合,使用命令模式 ...
- 设计模式14:Command 命令模式(行为型模式)
Command 命令模式(行为型模式) 耦合与变化 耦合是软件不能抵御变化的根本性原因.不仅实体对象与实体对象之间存在耦合关系,实体对象与行为操作之间也存在耦合关系. 动机(Motivation) 在 ...
- Command 命令模式
简介 将来自客户端的请求传入一个对象,从而使你可用不同的请求对客户进行参数化.用于[行为请求者]与[行为实现者]解耦,可实现二者之间的松耦合,以便适应变化. 将一个请求封装为一个对象,从而使你可用不同 ...
- 设计模式(十四):Command命令模式 -- 行为型模式
1.概述 在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来 ...
- 二十二、Command 命令模式
原理: 时序图: 代码清单: command.Command public interface Command { void execute(); } command.MacroCommand pub ...
- 设计模式C++学习笔记之十二(Command命令模式)
命令模式,将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化:对请求排队或记录请求日志,以及支持可撤消的操作.应该是一个比较简单的模式了. 12.1.解释 main(),客户 CIn ...
- 设计模式(14)--Command(命令模式)--行为型
作者QQ:1095737364 QQ群:123300273 欢迎加入! 1.模式定义: 命令模式属于对象的行为模式.命令模式又称为行动(Action)模式或交易(Transactio ...
- Command 命令模式 MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
随机推荐
- 数据可视化基础专题(十二):Matplotlib 基础(四)常用图表(二)气泡图、堆叠图、雷达图、饼图、
1 气泡图 气泡图和上面的散点图非常类似,只是点的大小不一样,而且是通过参数 s 来进行控制的,多的不说,还是看个示例: 例子一: import matplotlib.pyplot as plt im ...
- 数据可视化之powerBI入门(五)PowerQuery,支持从多种源导入数据
PowerBI的强大绝不仅是最后生成炫酷的可视化报告,她在第一步数据获取上就显示出了强大的威力,利用Power Query 的强大数据处理功能,几乎可以从任何来源.任何结构.任何形式上获取数据 htt ...
- 机器学习实战基础(十二):sklearn中的数据预处理和特征工程(五) 数据预处理 Preprocessing & Impute 之 处理分类特征:处理连续性特征 二值化与分段
处理连续性特征 二值化与分段 sklearn.preprocessing.Binarizer根据阈值将数据二值化(将特征值设置为0或1),用于处理连续型变量.大于阈值的值映射为1,而小于或等于阈值的值 ...
- It's time for Django
本节内容 Django流程介绍 Django url Django view Django models Django template Django form Django admin Django ...
- Python基础-类与对象
类的基本使用 class Person(): def __init__(self,name,age): self.name = name self.age = age def info(self): ...
- 那些非cmake生成的VTK工程存在的让人崩溃的坑
由于cmake和IDE的一些编译选项不同,所以导致我们使用者需要多做一些事情.而且vtk官方也表示: If you are not using CMake to compile your code, ...
- 【揭秘】C语言类型转换时发生了什么?
ID:技术让梦想更伟大 作者:李肖遥 链接:https://mp.weixin.qq.com/s/ZFf3imVaJgeesuhl1Kn9sQ 在C语言中,数据类型指的是用于声明不同类型的变量或函数的 ...
- Jpa常用注解@Test
/** * 客户的实体类 * @author zhy * * 明确使用的注解都是JPA规范的 * 所以导包都要导入javax.persistence包下的 * */ @Entity//表示当前类是一个 ...
- 全卷积神经网络FCN详解(附带Tensorflow详解代码实现)
一.导论 在图像语义分割领域,困扰了计算机科学家很多年的一个问题则是我们如何才能将我们感兴趣的对象和不感兴趣的对象分别分割开来呢?比如我们有一只小猫的图片,怎样才能够通过计算机自己对图像进行识别达到将 ...
- BUUCTF-web Easyweb
从这道题学到了挺多 一打开题目就是登陆页面,遂扫描文件检测是否存在文件泄露 用dirsearch扫出了robots.txt,.DS_Store和其他php.DS_Store没有可用信息(buuoj的题 ...