命令模式

来源:互联网 发布:java 设置语言环境 编辑:程序博客网 时间:2024/05/26 17:46

命令模式将请求封装成对象,以便使用不同的请求,队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。

现在有如下图遥控器。有3个可编程的插槽,以及对应的按钮。黑色按钮为开关按钮,有按下和弹起,两种状态。红色按钮为撤销按钮,撤销上一次操作,只有一种状态,按下立即弹起。
这里写图片描述

现在有两个电器。

// 电灯对象public class Light {    public void on(){        System.out.println("light on ...");    }    public void off(){        System.out.println("light off ...");    }}// 音响对象public class Stereo {    private String name;    private int volume;    public void on() {        System.out.println("stereo on ...");    }    public void off() {        System.out.println("stereo off ...");    }    public void insertCD(String name) {        this.name = name;        System.out.println("CD " + name + " insert ...");    }    public void takeCD() {        this.name = "";        System.out.println("CD " + name + " take ...");    }    public void setVolume(int volume) {        this.volume = volume;        System.out.println("volume set " + volume);    }}

类图

这里写图片描述

我们通过编程来让遥控器控制电器。

// 命令接口public interface Command {    void execute();    void undo();}// 空命令public class NoCommand implements Command {    @Override    public void execute() {        System.out.println("command is null");    }    @Override    public void undo() {        System.out.println("command is null");    }}// 开启电灯命令public class LightOn implements Command {    private Light light;    public LightOn(Light light) {        this.light = light;    }    @Override    public void execute() {        light.on();    }    @Override    public void undo() {        light.off();    }}// 电灯关闭命令public class LightOff implements Command {    private Light light;    public LightOff(Light light) {        this.light = light;    }    @Override    public void execute() {        light.off();    }    @Override    public void undo() {        light.on();    }}// 打开音响命令public class StereoOn implements Command {    private Stereo stereo;    public StereoOn(Stereo stereo) {        this.stereo = stereo;    }    @Override    public void execute() {        stereo.on();        stereo.insertCD("XXX CD");        stereo.setVolume(10);    }    @Override    public void undo() {        stereo.setVolume(0);        stereo.takeCD();        stereo.off();    }}// 关闭音响命令public class StereoOff implements Command {    private Stereo stereo;    public StereoOff(Stereo stereo) {        this.stereo = stereo;    }    @Override    public void execute() {        stereo.setVolume(0);        stereo.takeCD();        stereo.off();    }    @Override    public void undo() {        stereo.on();        stereo.insertCD("XXX CD");        stereo.setVolume(10);    }}// 宏命令public class Macro implements Command {    private List<Command> list;    public Macro(List<Command> list) {        this.list = list;    }    @Override    public void execute() {        for (Command command : list) {            command.execute();        }    }    @Override    public void undo() {        for (Command command : list) {            command.undo();        }    }}// 遥控器public class RemoteControl {    // 遥控器上插槽的数量    private int num = 3;    private Command[] onCommands;    private Command[] offCommands;    private Stack<Command> historyCommands;    public RemoteControl() {        onCommands = new Command[num];        offCommands = new Command[num];        historyCommands = new Stack<>();        Command noCommand = new NoCommand();  // 所有插槽初始化为同一个空对象        for (int i = 0; i < num; i++) {            onCommands[i] = noCommand;            offCommands[i] = noCommand;        }    }    public void setCommand(int slot, Command onCommand, Command offCommand) {        if (slot < num && slot > -1) {            onCommands[slot] = onCommand;            offCommands[slot] = offCommand;        } else {            throw new ArrayIndexOutOfBoundsException("slot is not exist");        }    }    // 按下按钮    public void buttonDown(int slot) {        if (slot < num && slot > -1) {            onCommands[slot].execute();            historyCommands.push(onCommands[slot]);        } else {            throw new ArrayIndexOutOfBoundsException("slot is not exist");        }    }    // 按钮弹起    public void buttonUp(int slot) {        if (slot < num && slot > -1) {            offCommands[slot].execute();            historyCommands.push(offCommands[slot]);        } else {            throw new ArrayIndexOutOfBoundsException("slot is not exist");        }    }    // 撤销按钮    public void undo() {        if (historyCommands.isEmpty()) {            System.out.println("do not have history command");        } else {            historyCommands.pop().undo();        }    }}// 测试类public class Test {    public static void main(String[] args) {        new Test().test();    }    private void test() {        // 遥控器对象        RemoteControl remoteControl = new RemoteControl();        // 各种家电        Light light = new Light();        Stereo stereo = new Stereo();        // 命令        LightOn lightOn = new LightOn(light);        LightOff lightOff = new LightOff(light);        StereoOn stereoOn = new StereoOn(stereo);        StereoOff stereoOff = new StereoOff(stereo);        // 宏命令        List<Command> onCommands = new ArrayList() {            {                add(lightOn);                add(stereoOn);            }        };        Macro macroOn = new Macro(onCommands);        List<Command> offCommands = new ArrayList() {            {                add(lightOff);                add(stereoOff);            }        };        Macro macroOff = new Macro(offCommands);        // 插入命令        remoteControl.setCommand(0, lightOn, lightOff);        remoteControl.setCommand(1, stereoOn, stereoOff);        remoteControl.setCommand(2, macroOn, macroOff);        // 操作遥控器        remoteControl.buttonDown(0);        remoteControl.buttonUp(0);        remoteControl.buttonDown(1);        remoteControl.buttonUp(1);        remoteControl.buttonDown(2);        remoteControl.buttonUp(2);        System.out.println(11111);        remoteControl.undo();        remoteControl.undo();        remoteControl.undo();    }}

注:

  • 我们把所有的电器的操作封装,暴露execute和undo方法。现在遥控器可以很方便的统一操作各种电器。
  • 将发出请求的对象和接受请求的对象解耦
  • 可以用来实现队列、日志、事务等
  • 队列:一个线程负责把命令封装放入对序列中,另一个线程从队列中取出命令,调用execute方法
  • 日志:把每次的操作记录下来,出现异常时读取日志,重新执行被记录的操作

参考文章
1. Head First 设计模式

0 0
原创粉丝点击