命令模式的两种不同实现

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

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

上图中,Invoker 类就相当于开发经理,ConcreteCommand 类是具体的命令,它继承自抽象命令类 Command 类,该抽象类中定义了每个命令被执行的方法 execute() 。Receiver 抽象类定义了对每一个具体的命令的执行方法 action() ,一旦接收到命令则立即行动。这里应该注意的是,每个具体的命令类都必须指定该命令的接收者,否则这命令发布了也没相应的人来完成,那就没戏了。
具体代码实现如下:
命令接收者相关类:

  1. //抽象接收者,定义了每个接收者应该完成的业务逻辑  
  2. abstract class AbstractReceiver {  
  3.     public abstract void doJob();  
  4. }  
  5.  
  6. // 具体接收者01,实现自己真正的业务逻辑  
  7. class Receiver01 extends AbstractReceiver {  
  8.     public void doJob() {  
  9.         System.out.println("接收者01 完成工作 ...\n");  
  10.     }  
  11. }  
  12.  
  13. // 具体接收者02,实现自己真正的业务逻辑  
  14. class Receiver02 extends AbstractReceiver {  
  15.     public void doJob() {  
  16.         System.out.println("接收者02 完成工作 ...\n");  
  17.     }  
命令类:

  1. // 抽象命令类,定义了每个具体命令被执行的入口方法execute()  
  2. abstract class AbstractCommand {  
  3.     public abstract void execute();  
  4. }  
  5.  
  6. // 具体命令类01,通过构造函数的参数决定了该命令由哪个接收者执行  
  7. class Command01 extends AbstsractCommand {  
  8.     private AbstractReceiver receiver = null;  
  9.  
  10.     public Command01(AbstractReceiver receiver) {  
  11.         this.receiver = receiver;  
  12.     }  
  13.  
  14.     public void execute() {  
  15.               System.out.println("命令01 被发布 ...");  
  16.         this.receiver.doJob();  
  17.     }  
  18. }  
  19.  
  20. // 具体命令类02,通过构造函数的参数决定了该命令由哪个接收者执行  
  21. class Command02 extends AbstractCommand {  
  22.     private AbstractReceiver receiver = null;  
  23.  
  24.     public Command02(AbstractReceiver receiver) {  
  25.         this.receiver = receiver;  
  26.     }  
  27.  
  28.     public void execute() {  
  29.               System.out.println("命令02 被发布 ...");  
  30.         this.receiver.doJob();  
  31.     }  
调用者类:

  1. // 调用者,负责将具体的命令传送给具体的接收者  
  2. class Invoker {  
  3.     private AbstractCommand command = null;  
  4.  
  5.     public void setCommand(AbstractCommand command) {  
  6.         this.command = command;  
  7.     }  
  8.  
  9.     public void action() {  
  10.         this.command.execute();  
  11.     }  
测试类:

  1. //测试类  
  2. public class Client {  
  3.     public static void main(String[] args) {  
  4.         // 创建调用者  
  5.         Invoker invoker = new Invoker();  
  6.  
  7.         // 创建一个具体命令,并指定该命令被执行的具体接收者  
  8.         AbstractCommand command01 = new Command01(new Receiver01());  
  9.  
  10.         // 给调用者发布一个具体命令  
  11.         invoker.setCommand(command01);  
  12.  
  13.         // 调用者执行命令,其实是将其传送给具体的接收者并让其真正执行  
  14.         invoker.action();  
  15.           
  16.         AbstractCommand command02 = new Command01(new Receiver02());  
  17.         invoker.setCommand(command02);  
  18.         invoker.action();  
  19.     }  
测试结果:
命令01 被发布 ...
接收者01 完成工作 ...
命令02 被发布 ...
接收者02 完成工作 ...
如上面测试中输出的结果,我们知道在客户端中每次声明并创建一个具体的命令对象时总要显式地将其指定给某一具体的接收者(也就是命令的最终执行者),这似乎不太灵活,在现实中有些命令的发布也确实不是预先就指定了该命令的接收者的。
我们可以修改一下类图,使得客户端在有必要的时候才显式地指明命令的接收者,如下:

较之第一个通用类图,这里的客户 Client 类不直接与接收者 Receiver 类相关,而仅仅与调用者 Invoker 类有联系,客户发布下来的一个命令或者请求,只需要到了调用者Invoker 这里就停止了,具体怎么实现,不必对客户公开,由调用者分配即可。这里的 Command 抽象类将子类中指定具体接收者的构造函数的逻辑提取出来,由具体子类提供通过调用父类构造函数的无参、有参构造函数来实现。主要修改的是命令相关的类。 
具体逻辑请看下面的代码实现:
命令类:

  1. /*  
  2.  * 抽象命令类,使用构造函数的传入参数预先内定具体接收者, 若想使用其他接收者,可在子类的构造函数中传入  
  3.  */ 
  4. abstract class AbstractCommand {  
  5.     protected AbstractReceiver receiver = null;  
  6.  
  7.     public AbstractCommand(AbstractReceiver receiver) {  
  8.         this.receiver = receiver;  
  9.     }  
  10.  
  11.     public abstract void execute();  
  12. }  
  13.  
  14. // 具体命令类01,提供无参、有参两种构造函数  
  15. class Command01 extends AbstractCommand {  
  16.  
  17.     // 使用无参构造函数来默认使用的具体接收者  
  18.     public Command01() {  
  19.         super(new Receiver01());  
  20.     }  
  21.  
  22.     // 使用有参构造函数来指定具体的接收者  
  23.     public Command01(AbstractReceiver receiver) {  
  24.         super(receiver);  
  25.     }  
  26.  
  27.     public void execute() {  
  28.               System.out.println("命令01 被发布 ...")  
  29.         this.receiver.doJob();  
  30.     }  
  31. }  
  32.  
  33. // 具体命令类02,提供无参、有参两种构造函数  
  34. class Command02 extends AbstractCommand {  
  35.  
  36.     // 使用无参构造函数来默认使用的具体接收者  
  37.     public Command02() {  
  38.         super(new Receiver02());  
  39.     }  
  40.  
  41.     // 使用有参构造函数来指定具体的接收者  
  42.     public Command02(AbstractReceiver receiver) {  
  43.         super(receiver);  
  44.     }  
  45.  
  46.     public void execute() {  
  47.               System.out.println("命令02 被发布 ...")  
  48.         this.receiver.doJob();  
  49.     }  
修改后的测试类:

  1. // 测试类  
  2. public class Client {  
  3.     public static void main(String[] args) {  
  4.         // 创建调用者  
  5.         Invoker invoker = new Invoker();  
  6.  
  7.         // 创建一个具体命令,并指定该命令被执行的具体接收者  
  8. //      AbstractCommand command01 = new Command01(new Receiver01());  
  9.         AbstractCommand command01 = new Command01();  
  10.  
  11.         // 给调用者发布一个具体命令  
  12.         invoker.setCommand(command01);  
  13.  
  14.         // 调用者执行命令,其实是将其传送给具体的接收者并让其真正执行  
  15.         invoker.action();  
  16.           
  17. //      AbstractCommand command02 = new Command01(receiver02);  
  18.         AbstractCommand command02 = new Command02();  
  19.         invoker.setCommand(command02);  
  20.         invoker.action();  
  21.           
  22.         System.out.println("\n设置命令01由接收者02执行...");  
  23.         command01 = new Command01(new Receiver02());  
  24.         invoker.setCommand(command01);  
  25.         invoker.action();  
  26.     }  
测试结果:
命令01 被发布 ...
接收者01 完成工作 ...
命令02 被发布 ...
接收者02 完成工作 ...
设置命令01由接收者02执行...
命令01 被发布 ...
接收者02 完成工作 ...

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/331145.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

tomcat(18)部署器

【0】README-1)先上干货:本文重点分析了tomcat 如何部署WAR文件的项目形式 以及 普通文件夹的项目形式;不管是WAR文件 还是 普通文件夹的项目形式,在tomcat中,它们都是Context容器;(Bingo&#…

装饰器模式和代理模式的区别

转载自 装饰器模式和代理模式的区别学习AOP时,教材上面都说使用的是动态代理,可是在印象中代理模式一直都是控制访问什么的,怎么又动态增加行为了,动态增加行为不是装饰器模式吗?于是找了很多资料,想弄清楚…

使用Java 8 Stream像操作SQL一样处理数据(上)

转载自 使用Java 8 Stream像操作SQL一样处理数据(上) 几乎每个Java应用都要创建和处理集合。集合对于很多编程任务来说是一个很基本的需求。举个例子,在银行交易系统中你需要创建一个集合来存储用户的交易请求,然后你需要遍历整个…

tomcat(19)Manager应用程序的servlet类

【0】README1)本文部分文字描述转自:“深入剖析tomcat”,旨在学习“tomcat(19)Manager应用程序的servlet类” 的相关知识;2)Manager应用程序用来管理已经部署的web 应用程序;在tomcat7中,manage…

使用Java 8 Stream像操作SQL一样处理数据(下)

转载自 使用Java 8 Stream像操作SQL一样处理数据(下) 在上一篇文章中,我们介绍了Stream可以像操作数据库一样来操作集合,但是我们没有介绍 flatMap 和 collect 操作。这两种操作对实现复杂的查询是非常有用的。比如你可以结果 fl…

spring(4)面向切面的Spring(AOP)

【0】README1)本文部分文字描述转自:“Spring In Action(中/英文版)”,旨在review “spring(4)面向切面的Spring(AOP)”的相关知识;2)在软件开发中,散布于应…

Mybatis-plus 思维导图,让 Mybatis-plus 不再难懂

转载自 Mybatis-plus 思维导图,让 Mybatis-plus 不再难懂 摘要: Mybatis-Plus(简称MP)是一个Mybatis的增强工具,在 Mybatis 的基础上只做增强不做改变,为简化开发、提高效率而生。mybatis-plus与mybatis mybatis Mybat…

报错:The type javax.servlet.http.HttpServletRequest cannot be resolved

【0】README 0.1)以下内容转自: http://blog.csdn.net/vincent080928/article/details/5392891 problem)The type javax.servlet.http.HttpServletRequest cannot be resolved. It is indirectly referenced from required .class files. 这…

MyBatis 思维导图,让 MyBatis 不再难懂(一)

转载自 MyBatis 思维导图,让 MyBatis 不再难懂(一)写在前面与hibernate相比,我无疑更喜欢mybatis,就因为我觉得它真的好用,哈哈。它简单上手和掌握;sql语句和代码分开,方便统一管理和…

tomcat(20)基于JMX的管理

【0】README1)本文部分文字描述转自:“深入剖析tomcat”,旨在学习“tomcat(20)基于JMX的管理” 的相关知识;2)晚辈我在tomcat上部署web 项目以测试JMX管理 tomcat 容器bean的效果,结果运行不成功&#xff0…

mybatis思维导图,让mybatis不再难懂(二)

转载自 mybatis思维导图,让mybatis不再难懂(二) 写在前面 上一篇文章写了mybatis的基本原理和配置文件的基本使用,这一篇写mybatis的使用,主要包括与sping集成、动态sql、还有mapper的xml文件一下复杂配置等。值得注意…

spring(5)构建 spring web 应用程序

【0】README1)本文部分文字描述转自:“Spring In Action(中/英文版)”,旨在review “spring(5)构建 spring web 应用程序” 的相关知识;【1】spring mvc 起步【1.1】跟踪spring mvc的请求1)请求…

Spring思维导图,让Spring不再难懂(ioc篇)

转载自 Spring思维导图,让Spring不再难懂(ioc篇) 写过java的都知道:所有的对象都必须创建;或者说:使用对象之前必须先创建。而使用ioc之后,你就可以不再手动创建对象,而是从ioc容器中…

spring(6) 渲染web视图

【0】README1)本文部分文字描述转自:“Spring In Action(中/英文版)”,旨在review “spring(6) 渲染web视图” 的相关知识;【1】 理解视图解析【1.1】视图解析的基础知识以及spring 提供的其他视图解析器1…

Spring思维导图,让Spring不再难懂(aop篇)

转载自 Spring思维导图,让Spring不再难懂(aop篇) 什么是aop AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程&…

tomcatSupplement(1)tomcat启动脚本分析(以Windows平台为例)

【0】README1)本文部分文字描述转自:“深入剖析tomcat”,旨在学习“tomcat启动脚本分析” 的相关知识;2)for tomcat4 startup files, please visit https://github.com/pacosonTang/HowTomcatWorks/tree/ma…

Spring思维导图,让spring不再难懂(一)

转载自 Spring思维导图,让spring不再难懂(一) 摘要: Spring是一个开源框架,是为了解决企业应用程序开发复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许您选择使用哪一个组件,同时为 J2EE …

spring(7)spring mvc 的高级技术

【0】README1)本文部分文字描述转自:“Spring In Action(中/英文版)”,旨在review “spring(7)spring mvc 的高级技术” 的相关知识;2)本文将会看到如何编写控制器来处理文件上传,如…

Ubuntu下MySQL、Redis以及MongoDB三个数据库的启动、重启以及停止命令

一、MySQL #启动 /etc/init.d/mysql start #停止 /etc/init.d/mysql stop #重启 /etc/init.d/mysql restart 二、Redis #启动 redis-server #停止 pkill redis-server三、MongoDB #启动服务 sudo service mongod start #重启 sudo service mongod restart #终止 sudo serv…

Spring思维导图,让Spring不再难懂(mvc篇)

转载自 Spring思维导图,让Spring不再难懂(mvc篇)spring mvc简介与运行原理Spring的模型-视图-控制器(MVC)框架是围绕一个DispatcherServlet来设计的,这个Servlet会把请求分发给各个处理器,并支持…