1. 概述 命令模式(Command Pattern)是一种行为设计模式,它将请求封装成对象,从而使你可以用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。
  2. 适用场景 需要将请求发送者和接收者解耦。 需要支持撤销操作。 需要记录请求日志。 需要支持事务操作。
  3. 命令模式的结构 命令模式涉及以下几个角色: Command(命令):声明执行操作的接口。 ConcreteCommand(具体命令):将一个接收者对象绑定于一个动作,调用接收者的相应操作,以实现Execute操作。 Receiver(接收者):知道如何实施与执行一个请求相关的操作。任何类都可能作为一个接收者。 Invoker(调用者):要求该命令执行这个请求。 Client(客户):创建具体命令对象并设定其接收者。
  4. 示例代码 假设我们有一个简单的电视遥控器系统,可以实现打开、关闭和调整音量的功能。 4.1 定义命令接口
public interface Command {
    void execute();
    void undo();
}
  • 1.
  • 2.
  • 3.
  • 4.

4.2 创建具体命令类

public class TVOnCommand implements Command {
    private TV tv;

    public TVOnCommand(TV tv) {
        this.tv = tv;
    }

    @Override
    public void execute() {
        tv.on();
    }

    @Override
    public void undo() {
        tv.off();
    }
}

public class TVOffCommand implements Command {
    private TV tv;

    public TVOffCommand(TV tv) {
        this.tv = tv;
    }

    @Override
    public void execute() {
        tv.off();
    }

    @Override
    public void undo() {
        tv.on();
    }
}

public class TVVolumeUpCommand implements Command {
    private TV tv;

    public TVVolumeUpCommand(TV tv) {
        this.tv = tv;
    }

    @Override
    public void execute() {
        tv.volumeUp();
    }

    @Override
    public void undo() {
        tv.volumeDown();
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.
  • 48.
  • 49.
  • 50.
  • 51.
  • 52.
  • 53.

4.3 创建接收者类

public class TV {
    private boolean on = false;
    private int volume = 0;

    public void on() {
        on = true;
        System.out.println("TV is on");
    }

    public void off() {
        on = false;
        System.out.println("TV is off");
    }

    public void volumeUp() {
        volume++;
        System.out.println("TV Volume: " + volume);
    }

    public void volumeDown() {
        volume--;
        System.out.println("TV Volume: " + volume);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.

4.4 创建调用者类

public class RemoteControl {
    private Command[] onCommands;
    private Command[] offCommands;
    private Command undoCommand;

    public RemoteControl() {
        onCommands = new Command[7];
        offCommands = new Command[7];

        Command noCommand = new NoCommand();
        for (int i = 0; i < 7; i++) {
            onCommands[i] = noCommand;
            offCommands[i] = noCommand;
        }
        undoCommand = noCommand;
    }

    public void setCommand(int slot, Command onCommand, Command offCommand) {
        onCommands[slot] = onCommand;
        offCommands[slot] = offCommand;
    }

    public void onButtonWasPushed(int slot) {
        onCommands[slot].execute();
        undoCommand = onCommands[slot];
    }

    public void offButtonWasPushed(int slot) {
        offCommands[slot].execute();
        undoCommand = offCommands[slot];
    }

    public void undoButtonWasPushed() {
        undoCommand.undo();
    }

    public String toString() {
        StringBuffer stringBuff = new StringBuffer();
        stringBuff.append("\n------ Remote Control -------\n");
        for (int i = 0; i < onCommands.length; i++) {
            stringBuff.append("[slot " + i + "] " + onCommands[i].getClass().getName()
                + "    " + offCommands[i].getClass().getName() + "\n");
        }
        stringBuff.append("[undo] " + undoCommand.getClass().getName() + "\n");
        return stringBuff.toString();
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.
  • 33.
  • 34.
  • 35.
  • 36.
  • 37.
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
  • 43.
  • 44.
  • 45.
  • 46.
  • 47.

4.5 创建空命令类

public class NoCommand implements Command {
    @Override
    public void execute() {}

    @Override
    public void undo() {}
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.

4.6 客户端代码

public class RemoteLoader {
    public static void main(String[] args) {
        RemoteControl remoteControl = new RemoteControl();

        TV tv = new TV();
        TVOnCommand tvOn = new TVOnCommand(tv);
        TVOffCommand tvOff = new TVOffCommand(tv);
        TVVolumeUpCommand tvVolumeUp = new TVVolumeUpCommand(tv);

        remoteControl.setCommand(0, tvOn, tvOff);
        remoteControl.setCommand(1, tvVolumeUp, tvVolumeUp);

        System.out.println(remoteControl);

        remoteControl.onButtonWasPushed(0);
        remoteControl.offButtonWasPushed(0);
        remoteControl.undoButtonWasPushed();
        remoteControl.onButtonWasPushed(1);
        remoteControl.undoButtonWasPushed();
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  1. 代码解释 Command 接口:定义了 execute 和 undo 方法,所有具体命令都需要实现这两个方法。 ConcreteCommand 类:实现了 Command 接口的具体命令类,每个命令类封装了一个接收者对象,并调用接收者的相应方法。 Receiver 类:知道如何实施与执行一个请求相关的操作,这里是 TV 类。 Invoker 类:调用者类 RemoteControl,它持有一系列命令对象,并在接收到请求时调用相应的命令对象的 execute 方法。 Client 类:客户端代码 RemoteLoader,负责创建具体命令对象并设置到调用者中。
  2. 优点 解耦:将请求的发送者和接收者解耦,使得请求的发送者不需要知道请求的接收者是谁,也不知道请求是如何被处理的。 扩展性:可以很容易地增加新的命令,而不会影响现有的代码。 支持撤销操作:通过在命令对象中保存状态,可以很容易地实现撤销操作。 支持日志记录:可以将命令对象序列化并存储起来,以便后续恢复。
  3. 缺点 增加类的数量:每个具体命令都需要一个单独的类来实现,这可能会导致类的数量增加。 复杂性:对于简单的操作,使用命令模式可能会显得过于复杂。