命令模式(Command Pattern)

命令模式(Command Pattern)是一种行为设计模式,它将一个请求封装为一个对象,从而使你可以使用不同的请求、队列或日志来参数化对象。命令模式让你可以在不修改调用对象的情况下将请求排队、记录日志或撤销操作。

核心思想:

  • 命令对象:将请求和其所有相关的参数封装到一个对象中。
  • 调用者:负责触发命令执行。
  • 接收者:负责执行具体的命令逻辑。
  • 优点:命令模式可以实现命令的撤销、重做、排队执行等功能。

关键组成:

  1. Command 接口:定义了一个执行命令的接口。
  2. 具体命令类:实现了 Command 接口,负责将请求委托给接收者执行。
  3. 接收者:实际执行命令操作的对象。
  4. 调用者:通过命令对象来执行请求,不需要直接调用接收者。

场景示例:家庭自动化系统

假设我们有一个家庭自动化系统,能够通过遥控器控制各种设备,比如灯和音响。我们将通过命令模式封装打开/关闭设备的操作。

代码实现:

1. 创建命令接口 Command

Command 接口定义了所有命令都必须实现的 execute() 方法。

public interface Command {
    void execute();
    void undo();  // 增加撤销功能
}
2. 创建接收者类

这些类代表具体的设备,如灯 (Light) 和音响 (Stereo),它们包含打开和关闭的具体操作。

2.1 Light
public class Light {
    public void on() {
        System.out.println("The light is on");
    }

    public void off() {
        System.out.println("The light is off");
    }
}
2.2 Stereo
public class Stereo {
    public void on() {
        System.out.println("The stereo is on");
    }

    public void off() {
        System.out.println("The stereo is off");
    }

    public void setCD() {
        System.out.println("CD is set in stereo");
    }

    public void setVolume(int volume) {
        System.out.println("Stereo volume set to " + volume);
    }
}
3. 创建具体的命令类

每个具体命令类都实现 Command 接口,将请求传递给对应的接收者对象执行。

3.1 LightOnCommand
public class LightOnCommand implements Command {
    private Light light;

    public LightOnCommand(Light light) {
        this.light = light;
    }

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

    @Override
    public void undo() {
        light.off();
    }
}
3.2 LightOffCommand
public class LightOffCommand implements Command {
    private Light light;

    public LightOffCommand(Light light) {
        this.light = light;
    }

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

    @Override
    public void undo() {
        light.on();
    }
}
3.3 StereoOnCommand
public class StereoOnCommand implements Command {
    private Stereo stereo;

    public StereoOnCommand(Stereo stereo) {
        this.stereo = stereo;
    }

    @Override
    public void execute() {
        stereo.on();
        stereo.setCD();
        stereo.setVolume(10);
    }

    @Override
    public void undo() {
        stereo.off();
    }
}
3.4 StereoOffCommand
public class StereoOffCommand implements Command {
    private Stereo stereo;

    public StereoOffCommand(Stereo stereo) {
        this.stereo = stereo;
    }

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

    @Override
    public void undo() {
        stereo.on();
        stereo.setCD();
        stereo.setVolume(10);
    }
}
4. 创建调用者类 RemoteControl

RemoteControl 作为调用者,保存命令对象并执行它们。

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(); // 空命令,避免 null 检查
        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 onButtonWasPressed(int slot) {
        onCommands[slot].execute();
        undoCommand = onCommands[slot];
    }

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

    public void undoButtonWasPressed() {
        undoCommand.undo();
    }
}
5. 创建空命令 NoCommand

空命令模式用于初始化每个命令插槽,防止出现 null 检查。

public class NoCommand implements Command {
    @Override
    public void execute() {
        // 什么也不做
    }

    @Override
    public void undo() {
        // 什么也不做
    }
}
6. 测试客户端

客户端测试如何通过命令模式控制设备。

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

        // 创建接收者
        Light livingRoomLight = new Light();
        Stereo stereo = new Stereo();

        // 创建命令对象
        LightOnCommand lightOn = new LightOnCommand(livingRoomLight);
        LightOffCommand lightOff = new LightOffCommand(livingRoomLight);
        StereoOnCommand stereoOn = new StereoOnCommand(stereo);
        StereoOffCommand stereoOff = new StereoOffCommand(stereo);

        // 将命令对象设置到遥控器中
        remoteControl.setCommand(0, lightOn, lightOff);
        remoteControl.setCommand(1, stereoOn, stereoOff);

        // 模拟按钮按下
        System.out.println("Turning on the light...");
        remoteControl.onButtonWasPressed(0);

        System.out.println("Turning off the light...");
        remoteControl.offButtonWasPressed(0);

        System.out.println("Undoing the last command...");
        remoteControl.undoButtonWasPressed();

        System.out.println("Turning on the stereo...");
        remoteControl.onButtonWasPressed(1);

        System.out.println("Turning off the stereo...");
        remoteControl.offButtonWasPressed(1);
    }
}

输出结果

Turning on the light...
The light is on
Turning off the light...
The light is off
Undoing the last command...
The light is on
Turning on the stereo...
The stereo is on
CD is set in stereo
Stereo volume set to 10
Turning off the stereo...
The stereo is off

代码解释

  1. 命令接口 Command:定义了命令的基本操作,包含 execute()undo() 方法。
  2. 具体命令类:实现了 Command 接口,负责调用接收者执行具体的命令操作。每个命令类负责一个具体的任务,如打开或关闭灯、打开或关闭音响等。
  3. 接收者类:如 LightStereo,这些类包含实际的业务逻辑。
  4. 调用者 RemoteControl:保存和调用命令对象,通过按钮调用 onCommandoffCommand
  5. 空命令 NoCommand:用来初始化按钮插槽,避免空指针异常。

优点

  • 松耦合:调用者和接收者之间通过命令对象解耦,调用者无需关心具体的业务实现。
  • 扩展性:可以轻松添加新的命令类而不影响其他类的代码。
  • 支持撤销和重做:命令模式通过封装命令对象,使得支持撤销和重做功能变得容易。

适用场景

  • 需要参数化对象以执行不同的请求。
  • 需要在不改变调用代码的情况下记录命令、撤销或排队执行命令。
  • 需要将操作封装为对象以便进行传递、记录、或回放。

通过命令模式,可以灵活地对操作进行解耦,适用于复杂系统中对操作的管理。

你可能感兴趣的:(Java,命令模式,java)