23种设计模式之命令(Command)模式

370 阅读2分钟

1、定义

将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化;对请求排队或者记录请求日志,以及支持可撤销的操作。

2、模式结构

命令模式由四部分组成:

  • Command(抽象命令角色):定义命令的接口,声明执行的方法。
  • ConcreteCommand(具体命令角色):命令接口实现对象,是“虚”的实现;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。
  • Receiver(接受者):真正执行命令的对象。
  • Invoker(调用者):要求命令对象执行请求,通常会持有命令对象,也可以持有很多的命令对象。这个是客户端真的触发命令并要求命令执行相应的地方,也就是是相当于使用命令对象的入口。

3、实例

3.1 Command

public interface Command {
    
    public void execute();
    
    // 撤销操作
    public void undo();
}

3.2 开灯/关灯/空命令(ConcreteCommand)

public class LightOnCommand implements Command {

    private LightReceiver light;
    
    public LightOnCommand(LightReceiver light) {
        this.light = light;
    }
    
    @Override
    public void execute() {
        // 调用接收者方法
        light.on();
    }
    
    @Override
    public void undo() {
        light.off();
    }
}
public class LightOffCommand implements Command {

    private LightReceiver light;
    
    public LightOffCommand(LightReceiver light) {
        this.light = light;
    }
    
    @Override
    public void execute() {
        // 调用接收者方法
        light.off();
    }
    
    @Override
    public void undo() {
        light.on();
    }
}
public class NoCommand implements Command {
    
    @Override
    public void execute() {
    }
    
    @Override
    public void undo() {
    }
}

3.3 LightReceiver(Receiver)

public class LightReceiver {
    
    public void on() {
        System.out.println("电灯打开了。。。");
    }
    
    public void off() {
        System.out.println("电灯关闭了。。。");
    }
}

3.4 遥控器(Invoker)

public class RemoteController {
    
    // 开关按钮的命令数组
    private Command[] onCommands;
    private Command[] offCommands;
    
    private Command undoCommand;
    
    // 完成按钮的初始化
    public RemoteController() {
        
        onCommands = new Command[5];
        offCommands = new Command[5];
        
        for(int i = 0; i < 5; i++) {
            onCommands[i] = new NoCommand();
            offCommands[i] = new NoCommand();
        }
    }
    
    // 设置你需要的命令
    public void setCommand(int no, Command onCommand, Command offCommand) {
        onCommands[no] = onCommand;
        offCommands[no] = offCommand;
    }
    
    // 按开按钮
    public void onButtonWasPushed(int no) {
        onCommands[no].execute();
        // 记住这次操作,用于撤销
        undoCommand = onCommands[no];
    }
    
    // 按关按钮
    public void offButtonWasPushed(int no) {
        offCommands[no].execute();
        // 记住这次操作,用于撤销
        undoCommand = offCommands[no];
    }
    
    // 撤销按钮
    public void undoButtonWasPushed() {
        undoCommand.undo();
    }
}

3.5 客户端调用

public class Client {

    public static void main(String[] args) {
        
        LightReceiver light = new LightReceiver();
        
        LightOnCommand lightOnCommand = new LightOnCommand(light);
        LightOffCommand lightOffCommand = new LightOffCommand(light);
        
        // 调用者
        RemoteController remoteController = new RemoteController();
        remoteController.setCommand(0, lightOnCommand, lightOffCommand);
        
        System.out.println("------按下开灯按钮------");
        remoteController.onButtonWasPushed(0);
        System.out.println("------按下关灯按钮------");
        remoteController.offButtonWasPushed(0);
        System.out.println("------按下撤销按钮------");
        remoteController.undoButtonWasPushed();
    }
}

4、适用场景

  • 需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。
  • 需要在不同的时间制定请求,将请求排队和执行请求。
  • 需要支持命令的撤销(Undo)操作和恢复(Redo)操作。
  • 徐亚奥将操作组合在一起,即支持宏命令。

5、在Spring中的应用

分析:

  • StatementCallback类似于命令接口(Command)。
  • QueryStatementCallback,匿名内部类,实现了命令接口,同时充当命令的接收者。
  • JdbcTemplate,命令调用者,其中execute方法中,调用了action.doInStatement方法。

6、优缺点

6.1 优点
  • 降低系统的耦合度。
  • 新的命令可以很容易地加入到系统中。
  • 可以比较容易地设计一个命令队列和宏命令(组合命令)。
  • 可以方便地实现对请求的Undo和Redo。
6.2 缺点
  • 可能会导致某些系统有过多的具体命令类。因为针对每一个命令都需要设计一个具体命令类,因为某些系统可能需要大量具体命令类,这将影响命令模式的使用。

源代码:github.com/freedom9/de…