• 设计模式之命令模式


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

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

      命令模式包含四种角色:

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

      命令接口:命令式一个接口,规定了用来封装“请求”的若干个方法,比如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();
        }
    }
    复制代码

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

  • 相关阅读:
    215. 数组中的第K个最大元素
    c++集合的操作
    201. 数字范围按位与
    150. 逆波兰表达式求值
    二叉树的遍历算法
    144. 二叉树的前序遍历
    139. 单词拆分 DP
    131. 分割回文串
    695. 岛屿的最大面积 DFS
    leetcode 200. 岛屿数量 DFS
  • 原文地址:https://www.cnblogs.com/future-wy/p/10609391.html
Copyright © 2020-2023  润新知