WHAT

命令模式 - Command Pattern

定义

Encapsulate a reuqest as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support ubdoable operations.

将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。

通用类图

Receiver - 接收者角色

干活的角色,命令传递到这里是应该被执行的。

通用Reveiver类
public abstract class Receiver {
	//抽象接受者,定义每个接受者都必须完成的业务
	public abstract void doSomething();
}
为什么Receiver是个抽象类

接收者可以有多个,有多个就需要定义一个所有特性的抽象集合——抽象的接收者。

具体的Reveiver类
public class ConcreteReceiver1 extends Receiver {
	//每个接收者都必须处理一定的业务逻辑
  @Override
	public void doSomething(){
	
	}
}

public class ConcreteReceiver2 extends Receiver {
	// 每个接收者都必须处理一定的业务逻辑
  @Override
	public void doSomething(){
	
	}
}

接收者可以是N个。

Command - 命令角色

需要执行的所有命令都在这里执行。

命令角色是命令模式的核心。

抽象的Command类
public abstract class Command {
  // 每个命令类都必须有一个执行命令的方法
  public abstract void execute();
}
具体的Command类
public class ConcreteCommand1 extends Command {
  // 对哪个Receiver类进行命令处理
  private Receiver receiver;
  //构造函数传递接收者
  public ConcreteCommand1(Receiver _receiver){
    this.receiver = _receiver;
  }
  //必须实现一个命令
  public void execute(){
    // 业务处理
    this.receiver.doSomething();
  }
}

public class ConcreteCommand2 extends Command {
  // 对哪个Receiver类进行命令处理
  private Receiver receiver;
  //构造函数传递接收者
  public ConcreteCommand2(Receiver _receiver){
    this.receiver = _receiver;
  }
  //必须实现一个命令
  public void execute(){
    // 业务处理
    this.receiver.doSomething();
  }
}

通过构造函数定义了该命令是针对哪个接收者发出,定义一个命令接收的主体。

Invoker - 调用者角色

接受到命令,并执行命令

调用者Invoker类
public class Invoker{
  private Command command;
  //接收命令
  public void setCommand(Command _command){
    this.command = _command;
  }
  //执行命令
  public void action(){
    this.command.execute();
  }
}

场景类

public class client{
  public static void main(String[] args){
    // 首先声明调用者Invoker
    Invoker invoker = new Invoker();
    //定义接收者
    Receiver receiver = new ConcreteReceiver1();
    //定义一个发送给接收者的命令
    Command command = new ConcreteCommand1(receiver);
    // 把命令交给调用者去执行
    invoker.setCommand(command);
    invoker.action();
  }
}

WHY

优势

类间解耦

封装性非常好,调用者角色与接收者角色之间没有任何依赖关系,调用者实现功能时只需调用Command抽象类的execute方法就可以了。

可扩展性

Command的子类可以非常容易地扩展,而调用者Invoker和高层次的模块Client不产生严重的代码耦合。

命令模式结合其他类型会更优秀

结合责任链模式,实现命令族解析任务;

结合模板方法模式,可以减少Command子类的膨胀问题

劣势

Command子类膨胀

当命令增多,Command子类也需要不断增加。N个命令需要N个Command子类。

HOW

使用场景

只要认为是命令的地方可以采用命令模式

扩展

反悔问题

两种方式解决:

结合备忘录模式还原最后状态

适合接收者为状态的变更情况,而不适合事件处理

通过增加一个新的命令

实现事件的回滚,增加命令的反命令。

最佳实践

建议在实际开发时采用封闭Receiver的方式,减少Client对Receiver的依赖

  • 抽象的Command类
public abstract class Command{
  // 定义一个子类的全局共享变量
  protected final Receiver receiver;
  //实现类必须定义一个接收者
  public Command(Receiver _receiver){
    this.receiver = _receiver;
  }
  // 每个命令类都必须有一个执行命令的方法
  public abstract void execute();
}

通过构造函数约定每个具体命令都必须指定一个接收者,也可以有多个接收者,使用集合类型。

  • 具体的Command类

    public class ConcreteCommand1 extends Command{
      // 声明自己的默认接收者
      public ConcreteCommand1(){
        super(new ConcreteReceiver1());
      }
      // 设置新的接收者
      public ConcreteCommand1(Receiver _receiver){
        super(_receiver);
      }
      // 每个具体的命令都必须实现一个命令
    	public void execute(){
        //业务处理
        super.receiver.doSomething();
      }
    }
    
    public class ConcreteCommand2 extends Command{
      // 声明自己的默认接收者
      public ConcreteCommand2(){
        super(new ConcreteReceiver2());
      }
      // 设置新的接收者
      public ConcreteCommand1(Receiver _receiver){
        super(_receiver);
      }
      // 每个具体的命令都必须实现一个命令
    	public void execute(){
        //业务处理
        super.receiver.doSomething();
      }
    }
    

    每个命令完成了单一的职责,而不是根据接收者的不同完成不同的职责。

    高增模块的调用不用考虑接收者是谁的问题。

  • 场景类

    public class client{
      public static void main(String[] args){
        // 首先声明调用者Invoker
        Invoker invoker = new Invoker();
        // 定义一个发送给接收者的命令
        Command command = new ConcreteCommand1();
        // 把命令交给调用者去执行
        invoker.setCommand(command);
        invoker.action();
    
      }
    }
    

《设计模式之禅》第十五章 命令模式