命令模式的两种不同实现

11人阅读 评论(0) 收藏 举报
分类:

转载自 命令模式(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)进行设计。
查看评论

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

命令模式(Command):将一个请求封装成一个对象,使得你用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。   命令模式,顾名思义来理解即可,就是客户端发布...
  • u012598110
  • u012598110
  • 2016-11-25 17:14:25
  • 223

使用RecyclerView实现两种不同Item布局

先上图看看效果:实现中间一个轮播图,下面多个相同Item的布局。我们都知道ListView有一个方法可以添加头布局,但是RecyclerView并没有提供这样的方法。那么怎么来实现呢??? 一、It...
  • m0_37602117
  • m0_37602117
  • 2017-09-21 14:17:41
  • 329

命令模式的更多用途:队列请求

命令可以将运算块打包(一个接受者和一组动作),然后将它传来传去,就像是一般的对象一样。现在,即使在命令对象被创建许久之后,运算依然可以被调用。事实上,它甚至可以在不同的线程中被调用。我们可以利用这样的...
  • u012822634
  • u012822634
  • 2015-04-13 13:31:27
  • 859

大话设计模式—命令模式

命令模式(Command Pattern)是一种数据驱动的设计模式,它属于行为型模式。请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该...
  • lmb55
  • lmb55
  • 2016-04-03 20:18:28
  • 1470

【游戏设计模式】之二 论撤消重做、回放系统的实现:命令模式

这篇文章将与大家聊一聊游戏开发中命令模式的用法。命令模式的成名应用是实现诸如撤消,重做,回放,时间倒流之类的功能。如果你想知道《Dota2》中的观战系统、《魔兽争霸3》中的录像系统、《守望先锋》的全场...
  • zhmxy555
  • zhmxy555
  • 2016-09-25 18:24:53
  • 21554

命令模式+备忘录模式,实现可撤销重做的计算器

老规矩,先上图再分析: 1.命令模式部分:     IOperation为命令的接收者,ICommond为命令接口,AbstractCommond实现了基础的命令功能,其他命令在其基础上拓展,...
  • amurocrash
  • amurocrash
  • 2014-12-02 16:29:25
  • 1421

JDK与设计模式:命令模式

1、命令模式       命令模式:将一个请求封装为一个对象,从而让我们可用不同的请求对客户进行参数化,用于“行为请求者”与“行为实现者”解耦,可实现二者之间的松耦合,以便适应变化。命令模式是一...
  • Luckydog1991
  • Luckydog1991
  • 2016-06-20 09:26:58
  • 1104

五分钟一个设计模式之命令模式

五分钟一个设计模式,用最简单的方法来描述设计模式。小米智能模块的例子前一段小米的老总雷军在印度的全英文演讲想必大家都还历历在目,不过今天我们讨论的主题不是那次演讲,而是小米智能模块。小米4发布时,雷军...
  • daguanjia11
  • daguanjia11
  • 2015-05-29 06:13:33
  • 1731

设计模式C++实现——命令模式

模式定义:         命令模式将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。         命令对象将动作和接受者包进对象中,这个对象只暴...
  • walkerkalr
  • walkerkalr
  • 2014-06-10 16:14:33
  • 1000

ViewPager实现左右两个屏幕的切换

项目终于需要这样的效果了,采用ViewPager去实现吧,让网上那些乱七八糟的屏幕切换类都终结了吧,ViewPager是google官方的! 下面是我借鉴的文章: 起初最早接触到左右两个屏幕切换的是...
  • gundumw100
  • gundumw100
  • 2017-04-10 18:40:22
  • 86
    个人资料
    持之以恒
    等级:
    访问量: 1万+
    积分: 457
    排名: 11万+
    文章存档