天天看點

設計模式學習筆記(六)——指令模式

定義: 将“請求”封裝為對象,以便使用不同的請求,隊列或者日志來參數化其他對象。指令模式也支援可撤銷的操作

UML類圖

設計模式學習筆記(六)——指令模式

Command:為所有的指令聲明了一個接口,調用指令對象的execute方法即可讓接收者進行相關動作。

ConcreteCommand:定義了動作和接收者之間的綁定關系,調用者隻需要調用其execute方法即可送出請求,然後由ConcreteCommand調用接收者的動作。

Receiver:接受者知道如何進行必要的工作,實作這個請求,任何類都可作為接收者。

Invoker:調用者持有一個Command引用,調用其execute方法送出請求。

分析: 指令對象将動作和接收者包進對象中,隻暴露出一個execute方法,當該方法被調用時,接收者就會進行這些動作,從外面看,其他對象并不知道究竟哪個接收者進行了哪些動作。

示例

有一個具有7個插槽的遙控器,每個插槽可以制定到不同的家電上,都有開和關按鈕,還有一個整體的撤銷功能。

接收者

public class Door {

    private String name;

    public Door(String name) {
        this.name = name;
    }

    public void on(){
        System.out.println(name+":打開門");
    }

    public void off(){
        System.out.println(name+":關上門");
    }
}

public class Light {

    private String name;

    public Light(String name) {
        this.name = name;
    }

    public void on(){
        System.out.println(name+":燈開了");
    }

    public void off(){
        System.out.println(name+":燈關了");
    }

}

public class Stereo  {

    private String name;

    public Stereo(String name) {
        this.name = name;
    }

    public void off(){
        System.out.println(name+":CD機關閉");
    }

    public void on(){
        System.out.println(name+":CD機打開");
    }

    public void setCD(){
        System.out.println("CD已插入");
    }

    public void setVolumn(int volumn){
        System.out.println("設定音量為:"+volumn);
    }
}
           

指令接口

public interface Command {
    void execute();
}
           

具體指令類

public class GarageDoorCloseCommand implements Command {

    private Door door;

    public GarageDoorCloseCommand(Door door) {
        this.door = door;
    }


    @Override
    public void execute() {
        door.off();
    }
}
public class GarageDoorOpenCommand implements Command {

    private Door door;

    public GarageDoorOpenCommand(Door door) {
        this.door = door;
    }

    @Override
    public void execute() {
        door.on();
    }
}
public class LightOffCommand implements Command {

    private Light light;

    public LightOffCommand(Light light) {

        this.light = light;
    }

    @Override
    public void execute() {
        light.off();
    }
}
public class LightOnCommand implements Command {

    private Light light;

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

    @Override
    public void execute() {
        light.on();
    }
}
public class NoCommand implements Command {
    @Override
    public void execute() {
        System.out.println("初始化");
    }
}
public class StereoOffCommand implements Command {

    private Stereo stereo;

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

    @Override
    public void execute() {
        stereo.off();
    }
}
public class StereoOnCommand implements Command {

    private Stereo stereo;

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

    @Override
    public void execute() {
        stereo.on();
        stereo.setCD();
        stereo.setVolumn(11);
    }
}
           

調用者

public class RemoteControl {

    private Command[] onCommands;
    private Command[] offCommands;

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

        NoCommand noCommand = new NoCommand();

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

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

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

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

    @Override
    public String toString() {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("\n---------遙控器----------\n");
        for(int i = 0;i < onCommands.length;i++){
            stringBuilder.append("[卡槽"+i+"]"+onCommands[i].getClass().getName()+"  "+"[卡槽"+i+"]"+offCommands[i].getClass().getName()+"\n");
        }
        return stringBuilder.toString();
    }
}
           

測試

public class RemoteLoader {

    public static void main(String[] args) {
        RemoteControl remoteControl = new RemoteControl();
        Light livingLight = new Light("卧室燈");
        Light kitchenLight = new Light("廚房燈");
        Door garageDoor = new Door("車庫門");
        Stereo stereo = new Stereo("欣欣");

        LightOnCommand livingLightOnCommand = new LightOnCommand(livingLight);
        LightOnCommand kitchenLightOnCommand = new LightOnCommand(kitchenLight);
        LightOffCommand livingLightOffCommand = new LightOffCommand(livingLight);
        LightOffCommand kitchenLightOffCommand = new LightOffCommand(kitchenLight);

        GarageDoorOpenCommand garageDoorOpenCommand = new GarageDoorOpenCommand(garageDoor);
        GarageDoorCloseCommand garageDoorCloseCommand = new GarageDoorCloseCommand(garageDoor);

        StereoOnCommand stereoOnCommand = new StereoOnCommand(stereo);
        StereoOffCommand stereoOffCommand = new StereoOffCommand(stereo);

        remoteControl.setCommand(0,livingLightOnCommand,livingLightOffCommand);
        remoteControl.setCommand(1,kitchenLightOnCommand,kitchenLightOffCommand);
        remoteControl.setCommand(2,garageDoorOpenCommand,garageDoorCloseCommand);
        remoteControl.setCommand(3,stereoOnCommand,stereoOffCommand);

        System.out.println(remoteControl);

        remoteControl.onButtonWasPushed(0);
        remoteControl.offButtonWasPushed(0);
        remoteControl.onButtonWasPushed(1);
        remoteControl.offButtonWasPushed(1);
        remoteControl.onButtonWasPushed(2);
        remoteControl.offButtonWasPushed(2);
        remoteControl.onButtonWasPushed(3);
        remoteControl.offButtonWasPushed(3);

    }
}
           
設計模式學習筆記(六)——指令模式

更多用途

1、隊列請求:實作指令接口的對象被放入隊列中,線程從隊列中提取指令對象,并将其從隊列中删除,調用其execute方法,完成動作,再去處理下一個指令對象。工作隊列對象并不在乎到底做些什麼工作,它們隻是取出指令對象,調用execute方法。

2、日志請求:通過在指令對象中添加store和load兩個方法,每調用一次execute,指令對象被store到磁盤上,如果系統出狀況,我們從本地中調用load方法,按次序批量執行指令對象的execute方法。