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。

参考于 https://gof.quanke.name/

公众号发哥讲

这是一个稍偏基础和偏技术的公众号,甚至其中包括一些可能阅读量很低的包含代码的技术文,不知道你是不是喜欢,期待你的关注。

如果你觉得文章还不错,就请点击右上角选择发送给朋友或者转发到朋友圈~

● 扫码关注我们

据说看到好文章不推荐的人,服务器容易宕机!

本文版权归发哥讲博客园共有,原创文章,未经允许不得转载,否则保留追究法律责任的权利。

 

22、Command 命令模式的更多相关文章

  1. Java设计模式(22)命令模式(Command模式)

    Command模式是最让我疑惑的一个模式,我在阅读了很多代码后,才感觉隐约掌握其大概原理,我认为理解设计模式最主要是掌握起原理构造,这样才对自己实际编程有指导作用.Command模式实际上不是个很具体 ...

  2. C++设计模式-Command命令模式

    Command命令模式作用:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化:对请求排队或记录请求日志,以及支持可撤销的操作. 由于“行为请求者”与“行为实现者”的紧耦合,使用命令模式 ...

  3. 设计模式14:Command 命令模式(行为型模式)

    Command 命令模式(行为型模式) 耦合与变化 耦合是软件不能抵御变化的根本性原因.不仅实体对象与实体对象之间存在耦合关系,实体对象与行为操作之间也存在耦合关系. 动机(Motivation) 在 ...

  4. Command 命令模式

    简介 将来自客户端的请求传入一个对象,从而使你可用不同的请求对客户进行参数化.用于[行为请求者]与[行为实现者]解耦,可实现二者之间的松耦合,以便适应变化. 将一个请求封装为一个对象,从而使你可用不同 ...

  5. 设计模式(十四):Command命令模式 -- 行为型模式

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

  6. 二十二、Command 命令模式

    原理: 时序图: 代码清单: command.Command public interface Command { void execute(); } command.MacroCommand pub ...

  7. 设计模式C++学习笔记之十二(Command命令模式)

      命令模式,将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化:对请求排队或记录请求日志,以及支持可撤消的操作.应该是一个比较简单的模式了. 12.1.解释 main(),客户 CIn ...

  8. 设计模式(14)--Command(命令模式)--行为型

    作者QQ:1095737364    QQ群:123300273     欢迎加入! 1.模式定义:   命令模式属于对象的行为模式.命令模式又称为行动(Action)模式或交易(Transactio ...

  9. Command 命令模式 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

随机推荐

  1. 数据可视化之分析篇(四)PowerBI分析模型:产品关联度分析

    https://zhuanlan.zhihu.com/p/64510355 逛超市的时候,面对货架上琳琅满目的商品,你会觉得这些商品的摆放,或者不同品类的货架分布是随机排列的吗,当然不是. 应该都听说 ...

  2. Show information of directory or disk

    There are so many commands of Ubuntu, we just need to know useful and high-frequency commands. I hav ...

  3. Windows故障转移群集(WSFC)的备份和恢复

    使用wbadmin进行备份和恢复将C盘数据备份到E盘查看备份的版本以及包含的items模拟群集角色被误删除进行恢复操作检查恢复的效果 WSFC群集的备份和恢复功能是使用Windows Server B ...

  4. typedef struct 指针结构体使用方法

    A>>>>>>>>>>>>>>>>>>>>>>>> ty ...

  5. 使用Python进行自动化测试

    目前大家对Python都有一个共识,就是他对测试非常有用,自动化测试里Python用途也很广,但是Python到底怎么进行自动化测试呢?今天就简单的向大家介绍一下怎么使用Python进行自动化测试,本 ...

  6. 虚拟DOM Vitural DOM Tree

      提起Virtual DOM,总是给人一种高深莫测的感觉,大家都知道它比DOM快.那么Virtual DOM到底是何方神圣呢?在深入理解Virtual DOM之前,先让我们回顾一下DOM. 一.什么 ...

  7. Spring RestTemplate 的介绍和使用-入门

    RestTemplate是什么? 传统情况下在java代码里访问restful服务,一般使用Apache的HttpClient.不过此种方法使用起来太过繁琐.spring提供了一种简单便捷的模板类来进 ...

  8. 【最短路+bfs+缩点】Paint the Grid Reloaded ZOJ - 3781

    题目: Leo has a grid with N rows and M columns. All cells are painted with either black or white initi ...

  9. Ubuntu Server 19.04配置静态IP

    这个/etc/netplan下默认有个文件50-cloud-init.yaml,直接修改它就行了 sudo vim /etc/netplan/50-cloud-init.yaml 网口名字enp0s3 ...

  10. Mysql concat() group_concat()用法

    数据库表: 关键字:concat 功能:将多个字符串连接成一个字符串 使用:concat(column1, column2,...)  字段中间可以加连字符 结果:连接参数产生的字符串,如果有任何一个 ...