【命令模式-Command】

来源:互联网 发布:编程数学 编辑:程序博客网 时间:2024/04/30 10:31
一、概述
      定义:
将一个请求封装成一个对象,从而可以使用不同的请求对客户端进行参数化;对请求排队或记录请求日志,并且可以提供命令的撤销和恢复功能。将“行为请求者”和“行为实现者”解耦,可实现二者之间的松耦合。
      队列请求:就是对命令对象进行排队,组成工作队列,然后依次取出命令对象来执行。可以使用多线程或者线程池来进行命令队列的处理,但是也可以使用一个线程,一次从队列中取出命令并执行该命令。
      日志请求:就是把请求的历史记录保存下来,一般是采用永久存储的方式。如果运行请求的过程中,系统崩溃了,那么在系统再次运行时,就可以从保存的历史记录里面获取日志请求,并重新执行命令。日志请求的实现有两种方案,一种是直接使用Java中的序列化方法(此方法较简单),另外一种就是在命令对象里面添加上存储和装载的方法,其实就是让命令对象自己实现类似序列化的功能。
        命令模式是关于怎样处理一个对象请求到另一个对象调用其方法完成某项任务的一种成熟的模式,这里称提出请求的对象为请求者,被请求的对象为接收者。在命令模式中,当一个对象请求另一个对象其调用方法时,不和被请求的对象直接打交道,而是把这这种请求封装到一个命令的对象中,封装的手段是将请求封装在命令对象的一个方法中。请求者发起请求,接受者调用方法。
二、命令模式结构图
      命令角色(Command):声明了用来封装请求的若干个方法,一般来说要对外公布一个execute方法等用来执行命令;
      具体命令角色(ConcreteCommand):将一个接收者对象绑定于一个动作;调用接收者相应的操作,以实现命令角色接口中所声明的方法;
      请求者角色(Invoker):请求者是一个包含“命令接口”变量的类的实例。请求者中的“命令接口”的变量可以存放任何具体命令的引用,请求者负责调用具体命令,让具体命令执行那些封装了请求的方法;
      接收者角色(Receiver):负责接收命令并且执行命令;
      客户角色(Client):创建一个具体命令对象(并可以设定它的接收者)。
      参入者之间的协作:
      1)Client创建一个ConcreteCommand对象并指定它的Receiver对象;
      2)Invoker对象存储该ConcreteCommand对象;
      3)Invoker通过调用Command对象的execute操作来提交一个请求。若该命令是可撤销的,ConcreteCommand在执行execute操作前存储当前状态以用于取消该命令;
      4)ConcreteCommand对象调用它的Receiver的操作以执行该请求。
三、示例代码
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public interface Command {  
  2.     // 执行命令  
  3.     public void execute();  
  4.   
  5.     // 撤销命令  
  6.     public void undo();  
  7. }  
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. //具体命令  
  2. public class ConcreteCommand implements Command {  
  3.     private Receiver receiver;  
  4.   
  5.     public ConcreteCommand(Receiver receiver) {  
  6.         this.receiver = receiver;  
  7.     }  
  8.   
  9.     @Override  
  10.     public void execute() {  
  11.         receiver.action();  
  12.     }  
  13.   
  14.     @Override  
  15.     public void undo() {  
  16.         receiver.unAction();  
  17.     }  
  18. }  
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. //命令接收者,命令真正执行人  
  2. public class Receiver {  
  3.   
  4.     public void action() {  
  5.         System.out.println("执行命令...");  
  6.     }  
  7.   
  8.     public void unAction() {  
  9.         System.out.println("撤销命令...");  
  10.     }  
  11. }  
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public class Invoker {  
  2.     // 调用者持有命令对象  
  3.     private Command command;  
  4.   
  5.     public Command getCommand() {  
  6.         return command;  
  7.     }  
  8.   
  9.     // 设置命令对象  
  10.     public void setCommand(Command command) {  
  11.         this.command = command;  
  12.     }  
  13.   
  14.     // 执行命令  
  15.     public void runCommand() {  
  16.         command.execute();  
  17.     }  
  18.   
  19.     // 撤销命令  
  20.     public void unDoCommand() {  
  21.         command.undo();  
  22.     }  
  23. }  
[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public class Client {  
  2.     public static void main(String[] args) {  
  3.         // 创建接受者  
  4.         Receiver receiver = new Receiver();  
  5.         // 创建命令对象,并设置它的接受者  
  6.         Command command = new ConcreteCommand(receiver);  
  7.         // 创建调用者,将命令对象设置进去  
  8.         Invoker invoker = new Invoker();  
  9.         invoker.setCommand(command);  
  10.         // 这里可以测试一下  
  11.         invoker.runCommand();  
  12.         invoker.unDoCommand();  
  13.     }  
  14. }  
四、优缺点
      优点:①
请求者不直接与接受者互交,既请求者不包含接受者的引用,因此彻底消除了彼此之间的耦合;满足了“开-闭原则”。如果增加新的具体命令和该命令的接受者,不必修改调用者的代码,调用者就可以直接使用新的命令对象。反之,如果增加新的调用者,不必修改现有的具体命令和接受者,新增加的调用者就可以使用已有的具体命令;由于请求者的请求被封装到了具体命令中,那么就可以将具体命令保存到持久化媒介中,在需要的时候重新执行这个具体命令,因此使用命令者模式可以记录日志;使用命令者模式可以对请求者的请求进行排队,每个请求者各自对应一个具体命令,因此可以按一定的顺序执行这些命令。简而言之,命令模式将调用操作的请求对象与知道如何实现该操作的接收对象解耦;具体命令角色可以被不同的请求者角色重用;可以将多个命令装配成一个复合命令;增加新的具体命令角色很容易,因为这无需改变已有的类。
     缺点:如果命令很多,开发起来就要头疼了。特别是很多简单的命令,实现起来就几行代码的事,而使用命令模式的话,不用管命令多简单,都需要写一个命令类来封装。 
五、应用场景
     1)使用命令模式作为"回调机制"在面向对象系统中的替代。"回调机制"讲的是先将一个函数登记上,以便在以后调用此函数;
     2)需要在不同的时间指定请求、将请求排队。一个命令对象和原先的请求发出者可以有不同的生命期。换言之,原先的请求发出者可能已经不在了,而命令对象本身仍然是活动的。这时命令的接收者可以是在本地,也可以在网络的另外一个地址。命令对象可以在串形化之后传送到另外一台机器上去;
     3)系统需要支持命令的撤消(undo)。命令对象可以把状态存储起来,等到客户端需要撤销命令所产生的效果时,可以调用undo()方法,把命令所产生的效果撤销掉。命令对象还可以提供redo()方法,以供客户端在需要时,再重新实施命令效果;
     4)如果一个系统要将系统中所有的数据更新到日志里,以便在系统崩溃时,可以根据日志里读回所有的数据更新命令,重新调用execute()方法依次执行这些命令,从而恢复系统在崩溃前所做的数据更新。
0 0
原创粉丝点击