命令模式(Java版)

这是我参与8月更文挑战的第10天,活动详情查看:8月更文挑战

什么是命令模式?

请求命令的形式包裹在对象中,并传给调用对象

调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。

请求、命令、动作等封装成对象,这样可以让项目使用这些对象来参数化其他对象,使得命令的请求者和执行者解耦。

项目背景

现在我们有一个家电自动化遥控器项目,要求设置遥控器各个按钮,控制家电的不同的设备。怎样设计自动化遥控器才能保证遥控器的扩展性好、维护性好呢?

代码示例

看上面的UML类型,我们定义了设备动作类设备执行命令类,以及设备控制类

设备动作类

首先是定义电器设备类。

灯类(可以开关):

public class Light {

	String loc = "";

	public Light(String loc) {
		this.loc = loc;
	}
        // 开灯
	public void on() {
		System.out.println(loc + " On");
	}
        // 关灯
	public void off() {
		System.out.println(loc + " Off");
	}

}
复制代码

音响类(可以开关,打开cd,调大和降低音量):

// 音响
public class Stereo {

    static int volume = 0;
    //调节音量
    public void setVol(int vol) {
        volume = vol;
        System.out.println("Stereo volume=" + volume);
    }

    public int getVol() {
        return volume;
    }

    public void on() {
        System.out.println("Stereo On");
    }

    public void off() {
        System.out.println("Stereo Off");
    }

    // CD 播放
    public void setCd() {
        System.out.println("Stereo setCd");
    }

}
复制代码

设备执行命令类

然后定义电器设备命令类。

设备命令抽象接口:

public interface Command {

	// 执行
	void execute();

	// 回退
	void undo();
}
复制代码

不执行任何设备命令:

public class NoCommand implements Command {

	@Override
	public void execute() {
	}

	@Override
	public void undo() {
	}

}
复制代码

关灯:

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();
    }

}
复制代码

开灯:

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();
    }

}
复制代码

关音响:

public class StereoOffCommand implements Command {
    private Stereo setreo;

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

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

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

}
复制代码

开音响:

public class StereoOnCommand implements Command {

    private Stereo setreo;

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

    @Override
    public void execute() {
        setreo.on();
        setreo.setCd();

    }

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

}
复制代码

音响-增加音量:

public class StereoAddVolCommand implements Command {

    private Stereo setreo;

    public StereoAddVolCommand(Stereo setreo) {
        this.setreo = setreo;
    }

    @Override
    public void execute() {
        int vol = setreo.getVol();
        if (vol < 11) {
            setreo.setVol(++vol);
        }
    }

    @Override
    public void undo() {
        int vol = setreo.getVol();
        if (vol > 0) {
            setreo.setVol(--vol);
        }
    }
}
复制代码

音响-音量降低:

public class StereoSubVolCommand implements Command {

    private Stereo setreo;

    public StereoSubVolCommand(Stereo setreo) {
        this.setreo = setreo;
    }

    @Override
    public void execute() {
        int vol = setreo.getVol();
        if (vol > 0) {
            setreo.setVol(--vol);
        }

    }

    @Override
    public void undo() {
        int vol = setreo.getVol();
        if (vol < 11) {
            setreo.setVol(++vol);
        }
    }
}
复制代码

如果我们一个按钮想控制多个设备,定义如下类。

命令列表类:

public class MarcoCommand implements Command {

    private Command[] commands;

    public MarcoCommand(Command[] commands) {
        this.commands = commands;
    }

    @Override
    public void execute() {
        for (int i = 0, len = commands.length; i < len; i++) {
            commands[i].execute();
        }
    }

    @Override
    public void undo() {
        for (int i = commands.length - 1; i >= 0; i--) {
            commands[i].undo();
        }
    }

}
复制代码

设备控制类

最后定义设备控制类。

按钮控制抽象接口:

public interface Control {

    // 按钮开
    void onButton(int slot);

    // 按钮关
    void offButton(int slot);

    // 按钮回退
    void undoButton();
}
复制代码

具体设备控制类:

import java.util.Stack;


public class CommandModeControl implements Control {

    private Command[] onCommands;

    private Command[] offCommands;
    
    // 将执行过得命令放入栈中
    private Stack<Command> stack = new Stack<Command>();

    //初始化所有按钮槽不执行任何操作
    public CommandModeControl() {
        
        onCommands = new Command[5];
        offCommands = new Command[5];

        Command noCommand = new NoCommand();
        for (int i = 0, len = onCommands.length; i < len; i++) {
            onCommands[i] = noCommand;
            offCommands[i] = noCommand;
        }
    }

    // 定义每个槽的打开按钮和关闭按钮
    public void setCommand(int slot, Command onCommand, Command offCommand) {
        onCommands[slot] = onCommand;
        offCommands[slot] = offCommand;
    }

    // 执行打开操作
    @Override
    public void onButton(int slot) {
        onCommands[slot].execute();
        stack.push(onCommands[slot]);
    }

    // 执行关闭操作
    @Override
    public void offButton(int slot) {
        offCommands[slot].execute();
        stack.push(offCommands[slot]);
    }

    // 回退
    @Override
    public void undoButton() {
        stack.pop().undo();
    }

}
复制代码

执行结果

命令测试:

public class ControlTest {

    public static void main(String[] args) {
        CommandModeControl control = new CommandModeControl();

        Light bedroomlight = new Light("卧室灯");
        Light kitchlight = new Light("厨房灯");

        LightOnCommand bedroomlighton = new LightOnCommand(bedroomlight);
        LightOffCommand bedroomlightoff = new LightOffCommand(bedroomlight);
        LightOnCommand kitchlighton = new LightOnCommand(kitchlight);
        LightOffCommand kitchlightoff = new LightOffCommand(kitchlight);

        // 批量操作灯的开关
        Command[] oncommands = {bedroomlighton, kitchlighton};
        Command[] offcommands = {bedroomlightoff, kitchlightoff};
        MarcoCommand onmarco = new MarcoCommand(oncommands);
        MarcoCommand offmarco = new MarcoCommand(offcommands);

        Stereo stereo = new Stereo();
        // 音响开关
        StereoOnCommand stereoOn = new StereoOnCommand(stereo);
        StereoOffCommand stereoOff = new StereoOffCommand(stereo);
        // 音响音量调节
        StereoAddVolCommand stereoaddvol = new StereoAddVolCommand(stereo);
        StereoSubVolCommand stereosubvol = new StereoSubVolCommand(stereo);

        // 卧室灯
        control.setCommand(0, bedroomlighton, bedroomlightoff);
        // 厨房灯
        control.setCommand(1, kitchlighton, kitchlightoff);
        // 音响开关
        control.setCommand(2, stereoOn, stereoOff);
        // 音响音量大小调节
        control.setCommand(3, stereoaddvol, stereosubvol);
        // 批量操作灯的开关
        control.setCommand(4, onmarco, offmarco);
        
        // 卧室开
        control.onButton(0);
        // 卧室关
        control.undoButton();

        // 厨房开
        control.onButton(1);
        // 厨房关
        control.offButton(1);
        
        // 音响开
        control.onButton(2);

        // 音响音量加
        control.onButton(3);
        // 音响音量减
        control.offButton(3);
        // 音响音量加
        control.undoButton();

        // 关音响
        control.offButton(2);
        // 开音响
        control.undoButton();
        
        // 批量开灯
        control.onButton(4);
        // 批量关关
        control.offButton(4);
    }

}
复制代码

输出结果:

卧室灯 On

卧室灯 Off

厨房灯 On

厨房灯 Off

Stereo On

Stereo setCd

Stereo volume=1

Stereo volume=0

Stereo volume=1

Stereo Off

Stereo On

Stereo setCd

卧室灯 On

厨房灯 On

卧室灯 Off

厨房灯 Off

总结

优点:

1、降低了系统耦合度。

2、新的命令可以很容易添加到系统中去。

缺点:

使用命令模式可能会导致某些系统有过多的具体命令类。

使用场景:

认为是命令的地方都可以使用命令模式,比如:

  1. GUI中每一个按钮都是一条命令。
  2. 模拟CMD。

注意:

如系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作,也可以考虑使用命令模式。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享