转载自 命令模式(Command)的两种不同实现

命令模式(Command):将一个请求封装成一个对象,使得你用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。
命令模式,顾名思义来理解即可,就是客户端发布一个命令(也就是“请求”),而这个命令是已经被封装成一个对象的。即这个命令对象的内部可能已经指定了该命令具体被谁负责执行。就像开发经理从客户那边获取对方的需求(命令),客户在描述具体的需求可以决定是否明确指出该需求的执行方。
命令模式的通用类图如下: 

上图中,Invoker 类就相当于开发经理,ConcreteCommand 类是具体的命令,它继承自抽象命令类 Command 类,该抽象类中定义了每个命令被执行的方法 execute() 。Receiver 抽象类定义了对每一个具体的命令的执行方法 action() ,一旦接收到命令则立即行动。这里应该注意的是,每个具体的命令类都必须指定该命令的接收者,否则这命令发布了也没相应的人来完成,那就没戏了。
具体代码实现如下:
命令接收者相关类:
  1. //抽象接收者,定义了每个接收者应该完成的业务逻辑
  2. abstract class AbstractReceiver {
  3. public abstract void doJob();
  4. }
  5. // 具体接收者01,实现自己真正的业务逻辑
  6. class Receiver01 extends AbstractReceiver {
  7. public void doJob() {
  8. System.out.println("接收者01 完成工作 ...\n");
  9. }
  10. }
  11. // 具体接收者02,实现自己真正的业务逻辑
  12. class Receiver02 extends AbstractReceiver {
  13. public void doJob() {
  14. System.out.println("接收者02 完成工作 ...\n");
  15. }
  16. }
命令类:
  1. // 抽象命令类,定义了每个具体命令被执行的入口方法execute()
  2. abstract class AbstractCommand {
  3. public abstract void execute();
  4. }
  5. // 具体命令类01,通过构造函数的参数决定了该命令由哪个接收者执行
  6. class Command01 extends AbstsractCommand {
  7. private AbstractReceiver receiver = null;
  8. public Command01(AbstractReceiver receiver) {
  9. this.receiver = receiver;
  10. }
  11. public void execute() {
  12. System.out.println("命令01 被发布 ...");
  13. this.receiver.doJob();
  14. }
  15. }
  16. // 具体命令类02,通过构造函数的参数决定了该命令由哪个接收者执行
  17. class Command02 extends AbstractCommand {
  18. private AbstractReceiver receiver = null;
  19. public Command02(AbstractReceiver receiver) {
  20. this.receiver = receiver;
  21. }
  22. public void execute() {
  23. System.out.println("命令02 被发布 ...");
  24. this.receiver.doJob();
  25. }
  26. }
调用者类:
  1. // 调用者,负责将具体的命令传送给具体的接收者
  2. class Invoker {
  3. private AbstractCommand command = null;
  4. public void setCommand(AbstractCommand command) {
  5. this.command = command;
  6. }
  7. public void action() {
  8. this.command.execute();
  9. }
  10. }
测试类:
  1. //测试类
  2. public class Client {
  3. public static void main(String[] args) {
  4. // 创建调用者
  5. Invoker invoker = new Invoker();
  6. // 创建一个具体命令,并指定该命令被执行的具体接收者
  7. AbstractCommand command01 = new Command01(new Receiver01());
  8. // 给调用者发布一个具体命令
  9. invoker.setCommand(command01);
  10. // 调用者执行命令,其实是将其传送给具体的接收者并让其真正执行
  11. invoker.action();
  12. AbstractCommand command02 = new Command01(new Receiver02());
  13. invoker.setCommand(command02);
  14. invoker.action();
  15. }
  16. }
测试结果:

命令01 被发布 ...
接收者01 完成工作 ...
命令02 被发布 ...
接收者02 完成工作 ...
如上面测试中输出的结果,我们知道在客户端中每次声明并创建一个具体的命令对象时总要显式地将其指定给某一具体的接收者(也就是命令的最终执行者),这似乎不太灵活,在现实中有些命令的发布也确实不是预先就指定了该命令的接收者的。
我们可以修改一下类图,使得客户端在有必要的时候才显式地指明命令的接收者,如下:

较之第一个通用类图,这里的客户 Client 类不直接与接收者 Receiver 类相关,而仅仅与调用者 Invoker 类有联系,客户发布下来的一个命令或者请求,只需要到了调用者Invoker 这里就停止了,具体怎么实现,不必对客户公开,由调用者分配即可。这里的 Command 抽象类将子类中指定具体接收者的构造函数的逻辑提取出来,由具体子类提供通过调用父类构造函数的无参、有参构造函数来实现。主要修改的是命令相关的类。 
具体逻辑请看下面的代码实现:
命令类:
  1. /*
  2. * 抽象命令类,使用构造函数的传入参数预先内定具体接收者, 若想使用其他接收者,可在子类的构造函数中传入
  3. */
  4. abstract class AbstractCommand {
  5. protected AbstractReceiver receiver = null;
  6. public AbstractCommand(AbstractReceiver receiver) {
  7. this.receiver = receiver;
  8. }
  9. public abstract void execute();
  10. }
  11. // 具体命令类01,提供无参、有参两种构造函数
  12. class Command01 extends AbstractCommand {
  13. // 使用无参构造函数来默认使用的具体接收者
  14. public Command01() {
  15. super(new Receiver01());
  16. }
  17. // 使用有参构造函数来指定具体的接收者
  18. public Command01(AbstractReceiver receiver) {
  19. super(receiver);
  20. }
  21. public void execute() {
  22. System.out.println("命令01 被发布 ...")
  23. this.receiver.doJob();
  24. }
  25. }
  26. // 具体命令类02,提供无参、有参两种构造函数
  27. class Command02 extends AbstractCommand {
  28. // 使用无参构造函数来默认使用的具体接收者
  29. public Command02() {
  30. super(new Receiver02());
  31. }
  32. // 使用有参构造函数来指定具体的接收者
  33. public Command02(AbstractReceiver receiver) {
  34. super(receiver);
  35. }
  36. public void execute() {
  37. System.out.println("命令02 被发布 ...")
  38. this.receiver.doJob();
  39. }
  40. }
修改后的测试类:
  1. // 测试类
  2. public class Client {
  3. public static void main(String[] args) {
  4. // 创建调用者
  5. Invoker invoker = new Invoker();
  6. // 创建一个具体命令,并指定该命令被执行的具体接收者
  7. //      AbstractCommand command01 = new Command01(new Receiver01());
  8. AbstractCommand command01 = new Command01();
  9. // 给调用者发布一个具体命令
  10. invoker.setCommand(command01);
  11. // 调用者执行命令,其实是将其传送给具体的接收者并让其真正执行
  12. invoker.action();
  13. //      AbstractCommand command02 = new Command01(receiver02);
  14. AbstractCommand command02 = new Command02();
  15. invoker.setCommand(command02);
  16. invoker.action();
  17. System.out.println("\n设置命令01由接收者02执行...");
  18. command01 = new Command01(new Receiver02());
  19. invoker.setCommand(command01);
  20. invoker.action();
  21. }
  22. }
测试结果:

命令01 被发布 ...
接收者01 完成工作 ...
命令02 被发布 ...
接收者02 完成工作 ...
设置命令01由接收者02执行...
命令01 被发布 ...
接收者02 完成工作 ...

此时在客户端中,我们不指明一个命令的具体接收者(执行者)也同样可以达到第一种实现方法中的效果。此外,客户也可以显式指出具体接收者,就像上面那样。

命令模式的优点:
1、 调用者与接收者没有任何的依赖关系,它们时通过具体的命令的存在而存在的;
2、 若有多个具体命令,只要扩展 Command 的子类即可,同样地具体接收者也可以相对应地进行扩展;
命令模式的缺点:其实上面优点中第2点在一定场景中也会变成缺点。如果具体的命令有很多个,那么子类就必然会暴增、膨胀。
但是,上面的具体代码实现中这种设计似乎也不太乐观,原因是每一个具体的命令都是由一个具体的接收者来执行的,在多交互的场景中这显然是太理想化的。于是,我想到了中介者模式(Mediator)中最主要的 Mediator 类中预先地注册了业务中需要交互的同事类的对象,接下来每一次交互逻辑都交给 Mediator 来“暗箱”操作。
根据上一段的想法,我们可以在抽象命令 Command 类中预先注册一定数量的具体接收者,那么具体命令中就可以决定是否要在多个接收者中进行协作完成了,这种协作的代码逻辑则应该写在覆盖父类的execute() 方法中,而在 execute() 方法中又可以运用模板方法模式(Template Method)进行设计。

命令模式的两种不同实现相关推荐

  1. 进Linux系统单用户模式,Linux进入单用户模式的两种方法

    单用户模式的作用 在使用Linux系统中,维护人员经常会碰到一个问题,就是在拥有root账号权限和密码的用户中,总是会出现忘记root密码的情况. 遇到这种情况,一般情况下,维护人员就会通过最常用的方 ...

  2. windows系统中,在当前目录下打开cmd命令行的两种方法

    windows系统中,在当前目录下打开cmd命令行的两种方法 1.在当前路径地址栏中直接输入'cmd',然后回车. 2.在当前路径下,按住'shift'键同时点击鼠标右键,点击"在此处打开P ...

  3. FPGA Verilog AD7606驱动代码,包含SPI模式读取和并行模式读取两种

    FPGA Verilog AD7606驱动代码,包含SPI模式读取和并行模式读取两种,代码注释详细 编号:7428665912784264白衫如初oh

  4. PPT“放映模式”的两种设置方法

    做好的PPT文件要如何放映呢?下面来说说设置PPT放映模式的两种方法. 方法一,直接在PPT文件里设置播放幻灯片. 1.打开PPT后,点击菜单栏[幻灯片放映]选项下的[设置幻灯片放映]. 2.弹出对话 ...

  5. ftp服务器的运行模式,FTP两种模式详解和实践技巧

    笔者最近几天被FTP折腾了一下,简单的路由器ACL就是无法通过被动模式的FTP流量.经数次实验和度娘.谷哥指导,终于对FTP模式有了深入理解.先感叹一句,以前学艺不精,书到用时方恨少! 不必要对FTP ...

  6. linux怎么配置命令模式,Linux几种命令模式

    原标题:Linux几种命令模式 必须掌握的几个Linux命令 我们为什么要学习Linux的命令? 初学者上手Linux系统会很懵.不管你是否安装了图形化界面,或者去网上找一些教程.或者一些Linux的 ...

  7. SSH远程管理、参数讲解、xshell使用、scp,sftp,ssh命令(ssh两种方式的密钥验证方...

    1.SSH远程管理 SSH(Secure Sheel)是一种安全通道协议,主要用于实现字符界面的远程登录.远程复制等功能.SSH协议对通信双方的数据传输进行了加密处理,其中包括用户的口令.与早期的TE ...

  8. PPT设置“只读模式”的两种方法

    想要防止PPT文件被意外更改,或者禁止他人随意更改,我们可以给PPT设置保护模式,而PPT的"只读模式"就起到了这样的作用. ​具体的设置方法有两种,我们可以根据不同需求选择合适的 ...

  9. 设置Excel表格“只读模式”的两种方法

    Excel表格的"只读模式"可以帮助我们防止意外更改表格,根据不同需求,表格可以设置"有密码"和"无密码"的两种"只读模式&quo ...

最新文章

  1. php安装pear和phpunit
  2. ios学习——键盘的收起
  3. mysql 更改数据库编码_更改MySQL数据库的编码为utf8mb4
  4. pcb成型板aoi检测_缺陷检测 | PCB AOI质量检测之自动定位核选取算法
  5. 取某个日期所在周的任意一天日期
  6. boost::math模块两个 Lambert W 函数的最基本调用示例
  7. c++ 标准库中 cin.ignore()
  8. SPOJ- QTREE+HDU 3966(树链剖分裸题
  9. 小甲鱼 OllyDbg 教程系列 (一) :二进制破解科普系列之 ReverseMe
  10. html颜色代码表_html颜色代码表
  11. java txt中统计一个字母出现的次数并储存,统计txt文件中每个字符出现的次数,并根据次数从高到低排序...
  12. PS使用:windows解决Adobe Photoshop 2020(PS2020)闪退
  13. 6种展示代码的绝佳方式
  14. 基于SSM的医院病历管理系统
  15. c#实现Udp通信(四)--UPD大数据量接收(异步接收)
  16. python制作qq登录界面_用Python实现一个最新QQ办公版(TIM)的登录界面
  17. MySQL面试题——聚簇索引和非聚簇索引
  18. 简单的python小程序祝福母亲,母亲节快乐!
  19. 俄罗斯方块源代码Github
  20. 洞口四中2021高考成绩查询,常德高考成绩查询入口2021

热门文章

  1. 《C++ Primer》7.2节练习
  2. 洛谷 P1596 [USACO10OCT]Lake Counting S-dfs
  3. Zookeeper实践与应用--分布式锁实现
  4. python正态分布函数_python3-正态分布
  5. P2596 [ZJOI2006]书架(fhq treap)
  6. 观星(计算几何/凸包/多边形面积)
  7. P4239 任意模数多项式乘法逆(多项式/ MTT)
  8. Codeforces Round #715 (Div. 2) C. The Sports Festival 区间dp
  9. 斜堆学习笔记+复杂度证明
  10. 【NOI2013】快餐店【基环树】【树的直径】【set】