设计模式之命令模式

  在许多设计中,经常设计一个对象请求另一个对象执行某一个操作。如果请求者无法或者不希望直接和被请求者打交道,即请求对象无法或者不希望含有被请求者的引用,那么可以使用命令模式。命令模式里称提出请求的对象为请求者,被请求者的对象为接收者。在命令模式中,当一个对象请求另一个对象调用其方法时,不和被请求者直接打交道,而是把这种“请求”封装到一个“命令”对象中,封装的手段将“请求”封装到“命令”对象的一个方法中。命令模式的核心就是使用命令对象来封装方法调用。

  例如,在军队作战时,指挥官要命令三连偷袭敌人。但是指挥官这时无法或者不希望和三连直接取得联系,那么指挥官可以发出一个命令,把该命令的执行者设置为三连。这样指挥官只要和命令打交道。

  命令模式包含四种角色:

  接收者:接收者是一个类的实例,该实例负责执行与请求相关的操作。

  命令接口:命令式一个接口,规定了用来封装“请求”的若干个方法,比如execute()、undo()等方法。

  具体命令:具体命令式实现了命令接口的类的实例,包含命令接口的方法。并且包含接受者的引用,指明那个对象去执行该命令。

  请求者:请求者是包含命令接口变量类的实例,该接口变量可以存放任何具体命令的引用。请求者负责调用具体命令,让具体命令执行那些封装了的请求方法。

下面看军队作战的例子:

  1.命令接收者:

1 package com.command;
2 //命令接收者、执行者
3 public class CompanyArmy {
4     public void sneakAttack(){
5         System.out.println("我们知道如何袭击敌人,保证完成任务");
6     }
7 }

  2.命令接口:

1 package com.command;
2 //命令接口
3 public interface Command {
4     void execute();
5 }

  3.具体命令:

 1 package com.command;
 2 //具体命令
 3 public class ConcreteCommand implements Command{
 4     CompanyArmy army;  //含有接收者的引用
 5     public ConcreteCommand(CompanyArmy army) {
 6         this.army = army;
 7     }
 8     public void execute() {   //封装着指挥官的请求
 9         army.sneakAttack();   //偷袭敌人
10     }
11 }

  4.请求者:

 1 package com.command;
 2 //请求者,也就是命令发送者
 3 public class ArmySuperior {
 4     Command command;     //存放具体命令的引用
 5     public void setCommand(Command command){
 6         this.command = command;
 7     }
 8     public void startExecuteCommand(){
 9         command.execute();
10     }
11 }

  命令模式所需要的四个角色已经建立好了,下面测试一下:

 1 package com.command;
 2 
 3 public class Application {
 4     public static void main(String[] args) {
 5         CompanyArmy army = new CompanyArmy();   //创建命令接收者
 6         Command command = new ConcreteCommand(army);//创建一个具体命令并且指定接收者
 7         ArmySuperior superior = new ArmySuperior();    //创建命令请求者
 8         superior.setCommand(command);   //给请求者设置一个具体命令
 9         superior.startExecuteCommand();   //开始执行命令
10     }
11 }

  这样就可以实现指挥官不直接和命令执行者,只需要发送一个命令,该命令就会找到指定的执行者去执行。这样大大降低了程序的耦合度。另外还有一个好处就是,要想发送另外一个命令只要再创建一个具体的命令即可,不需要修改其他代码,增强了程序的扩展性。

  使用命令模式还有一个好处,就是可以撤销所执行的操作。也就是请求者发送一个请求,接收者执行后,还可以撤销该操作。以下使用一个简单的例子说明怎样在具体的命令中实现undo()方法。问题如下:

  请求者请求在硬盘建立一个目录,请求成功后海可以撤销请求。这就要求接收者不仅可以在硬盘上建立目录,海可以删除上一次建立的目录。

  1接收者:要包含两个方法,建立目录和删除目录

 1 package com.command1;
 2 import java.io.File;
 3 //命令接收者
 4 public class MakeDir {
 5     //创建目录
 6     public void createDir(String name){
 7         File dir = new File(name);
 8         dir.mkdir();
 9     }
10     //删除目录
11     public void deleteDir(String name){
12         File dir = new File(name);
13         dir.delete();
14     }
15 }

 

  2.命令接口

1 package com.command1;
2 //包含撤销的命令接口
3 public interface Command {
4     void execute(String name);
5     void undo();
6 }

 

  3.具体命令

 1 package com.command1;
 2 import java.util.ArrayList;
 3 //具体命令
 4 public class ConcreteCommand implements Command {
 5     ArrayList<String> dirNameList;
 6     MakeDir makeDir;
 7     public ConcreteCommand(MakeDir makeDir) {
 8         dirNameList = new ArrayList<String>();
 9         this.makeDir = makeDir;
10     }
11     public void execute(String name) {
12         makeDir.createDir(name);
13         dirNameList.add(name);
14     }
15     public void undo() {
16         if(dirNameList.size()>0){
17             makeDir.deleteDir(dirNameList.get(dirNameList.size()-1));
18             dirNameList.remove(dirNameList.size()-1);
19         }else{
20             System.out.println("没有需要撤销的操作!");
21         }
22     }
23 }

 

  4.请求者

 1 package com.command1;
 2 //请求者
 3 public class RequestMakeDir {
 4     Command command;
 5     public void setCommand(Command command) {
 6         this.command = command;
 7     }
 8     public void startExecuteCommand(String name){
 9         command.execute(name);
10     }
11     public void undoCommand(){
12         command.undo();
13     }
14 }

  包含撤销的命令模式的四个角色创建好了,下面写一个测试类:

package com.command1;

public class Application {
    public static void main(String[] args) {
        MakeDir makeDir = new MakeDir();  //创建接收者
        Command command = new ConcreteCommand(makeDir);//创建具体命令并且指定接收者
        RequestMakeDir request = new RequestMakeDir(); //创建请求者
        request.setCommand(command);   //设置命令
        request.startExecuteCommand("haha");  //创建目录
        request.startExecuteCommand("hahaa");
        request.undoCommand();  //撤销
        request.undoCommand();
    }
}

  这样就可以创建目录和撤销操作了。

  

posted @ 2013-04-15 20:41  残剑_  阅读(1911)  评论(3编辑  收藏  举报