上一次留给大家去做的实践, 不知道大家执行的怎么样了呢.
我们通过一个简单的练习, 完成了一个控制开关. 那现在, 我们打算将遥控器的每个插槽, 对应到一个命令这样就要遥控器变成 "调用者". 当按下按钮, 相应命令对象的 execute()方法就会被调用, 其结果就是, 接收者 (例如电灯, 风扇, 音响) 的动作被调用.
实现遥控器
- public class RemoteControl {
- Command[] onCommands;
- Command[] offCommands;
- 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;
- }
- }
- // 这个方法有三个参数, 分别是插槽的位置, 开的命令, 关的命令. 这些命令将记录开关数组中对应的插槽位置, 以供稍后使用
- public void setCommand(int slot, Command onCommand, Command offCommand) {
- onCommands[slot] = onCommand;
- offCommands[slot] = offCommand;
- }
- // 当按下开或关的按钮, 硬件就会负责调用对应的方法, 也就是 onButtonWasPushed 或 offButtonWasPushed
- public void onButtonWasPushed(int slot) {
- onCommands[slot].execute();
- }
- public void offButtonWasPushed(int slot) {
- offCommands[slot].execute();
- }
- 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");
- }
- return stringBuff.toString();
- }
- }
实现命令
此前我们已经动手实现过 LightOnCommand, 纯粹就是简单的开和关命令. 那现在, 我们来为音响编写开与关的命令.
音响的关闭是毫无难度, 就是开启的时候有点复杂, 你知道为什么吗? 难道音响开了就好了? 是否还需要后续其他的动作才能让音响响起来了? 哎呀, 小编多嘴了好像.
- public class StereoOnWithCDCommand implements Command {
- Stereo stereo;
- public StereoOnWithCDCommand(Stereo stereo) {
- this.stereo = stereo;
- }
- // 打开音响, 需要三个步骤, 开启音响, 设置 CD 播放, 设置音量, 不然就成哑巴了
- public void execute() {
- stereo.on();
- stereo.setCD();
- stereo.setVolume(11);
- }
- }
这里列举了一个电灯, 一个音响, 差不多就把其他类似的都已经搞定了, 比如电扇, 门, 对吧. 所以, 赶紧看看你之前动手的操作, 是不是和小编的差不多.
让我们继续看下, 多个的是怎么实现的呢.
- public class RemoteLoader {
- public static void main(String[] args) {
- RemoteControl remoteControl = new RemoteControl();
- // 将所有的装置创建在合适的位置
- Light livingRoomLight = new Light("Living Room");
- Light kitchenLight = new Light("Kitchen");
- CeilingFan ceilingFan= new CeilingFan("Living Room");
- GarageDoor garageDoor = new GarageDoor("");
- Stereo stereo = new Stereo("Living Room");
- // 创建所有的电灯命令对象
- LightOnCommand livingRoomLightOn =
- new LightOnCommand(livingRoomLight);
- LightOffCommand livingRoomLightOff =
- new LightOffCommand(livingRoomLight);
- LightOnCommand kitchenLightOn =
- new LightOnCommand(kitchenLight);
- LightOffCommand kitchenLightOff =
- new LightOffCommand(kitchenLight);
- // 创建吊扇的开与关命令
- CeilingFanOnCommand ceilingFanOn =
- new CeilingFanOnCommand(ceilingFan);
- CeilingFanOffCommand ceilingFanOff =
- new CeilingFanOffCommand(ceilingFan);
- // 创建车库门的上与下命令
- GarageDoorUpCommand garageDoorUp =
- new GarageDoorUpCommand(garageDoor);
- GarageDoorDownCommand garageDoorDown =
- new GarageDoorDownCommand(garageDoor);
- // 创建音响的开与关命令
- StereoOnWithCDCommand stereoOnWithCD =
- new StereoOnWithCDCommand(stereo);
- StereoOffCommand stereoOff =
- new StereoOffCommand(stereo);
- // 现在已经有了全部的命令, 我们将它们加载到遥控器插槽中
- remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff);
- remoteControl.setCommand(1, kitchenLightOn, kitchenLightOff);
- remoteControl.setCommand(2, ceilingFanOn, ceilingFanOff);
- remoteControl.setCommand(3, stereoOnWithCD, stereoOff);
- 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);
- }
- }
写文档的时候到了
我们这个主要的设计目标就是让遥控器代码尽可能地简单, 这样一来, 新的厂商类一旦出现, 遥控器并不需要随之修改. 因为, 我们才用了命令模式, 从逻辑上将遥控器的类和厂商的类解耦. 我们相信这将降低遥控器的生产成本, 并大大地减少维护时所需的费用.
下面的类图提供了设计的全貌:
撤销哪去了?
别急别急, 小编说的功能都会有的. 撤销功能使用起来就是这样的: 比如说客厅的电灯是关闭的, 然后你按下遥控器上的开启按钮, 自然电灯就被打开了. 现在如果按下撤销按钮, 那么上一个动作将被倒转, 在这个例子里, 电灯将被关闭.
同样, 我们先来一个简单的撤销示例. 之前我们用的是 execute()方法实现开启或者关闭的调用, 那么我们用 undo()方法来执行撤销操作. 即在 Command 接口里实现一个同 execute()相反的方法 undo(), 然后在实现类里将 undo()的动作做成和 execute()相反的操作即可.
讲的有点笼统? 在这里小编就不提供具体的代码了, 详细的请看 GitHub 我的分享吧.
使用状态实现撤销
因为电灯这个开关已经撤销, 是很简单的入门, 小编没有提供源码在文中, 但是因为还有电风扇这个存在, 小编还不得不继续搞一个高大上的方式. 电扇不仅仅是开关, 还有档位的存在, 对吧, 是不是瞬间有思路了呢?
- public class CeilingFan {
- public static final int HIGH = 3;
- public static final int MEDIUM = 2;
- public static final int LOW = 1;
- public static final int OFF = 0;
- String location;
- int speed;
- public CeilingFan(String location) {
- this.location = location;
- speed = OFF;
- }
- public void high() {
- speed = HIGH;
- System.out.println(location + "ceiling fan is on high");
- }
- public void medium() {
- speed = MEDIUM;
- System.out.println(location + "ceiling fan is on medium");
- }
- public void low() {
- speed = LOW;
- System.out.println(location + "ceiling fan is on low");
- }
- public void off() {
- speed = OFF;
- System.out.println(location + "ceiling fan is off");
- }
- public int getSpeed() {
- return speed;
- }
- }
现在我们就来实现风扇的撤销. 这么做, 需要追踪吊扇的最后设置速度, 如果 undo 方法被调用了, 就要恢复成之前吊扇速度的设置值. 就如下面这样:
- public class CeilingFanHighCommand implements Command {
- CeilingFan ceilingFan;
- // 增加局部状态以便追踪吊扇之前的速度
- int prevSpeed;
- public CeilingFanHighCommand(CeilingFan ceilingFan) {
- this.ceilingFan = ceilingFan;
- }
- public void execute() {
- // 我们改变吊扇的速度之前, 需要先将它之前的状态记录起来, 以便需要撤销时使用
- prevSpeed = ceilingFan.getSpeed();
- ceilingFan.high();
- }
- // 将吊扇的速度设置会之前的值, 达到撤销的目的
- public void undo() {
- if (prevSpeed == CeilingFan.HIGH) {
- ceilingFan.high();
- } else if (prevSpeed == CeilingFan.MEDIUM) {
- ceilingFan.medium();
- } else if (prevSpeed == CeilingFan.LOW) {
- ceilingFan.low();
- } else if (prevSpeed == CeilingFan.OFF) {
- ceilingFan.off();
- }
- }
- }
让我们来测试下风扇吧
条件都具备了, 那我们来测试下吧. 我们打算把 0 号插槽的开启按钮设置为中速, 把第 1 号插槽的开启按钮设置成高速, 代码如下:
- public class RemoteLoader {
- public static void main(String[] args) {
- RemoteControlWithUndo remoteControl = new RemoteControlWithUndo();
- CeilingFan ceilingFan = new CeilingFan("Living Room");
- CeilingFanMediumCommand ceilingFanMedium =
- new CeilingFanMediumCommand(ceilingFan);
- CeilingFanHighCommand ceilingFanHigh =
- new CeilingFanHighCommand(ceilingFan);
- CeilingFanOffCommand ceilingFanOff =
- new CeilingFanOffCommand(ceilingFan);
- remoteControl.setCommand(0, ceilingFanMedium, ceilingFanOff);
- remoteControl.setCommand(1, ceilingFanHigh, ceilingFanOff);
- // 首先, 我们以中速开启吊扇
- remoteControl.onButtonWasPushed(0);
- // 然后关闭
- remoteControl.offButtonWasPushed(0);
- System.out.println(remoteControl);
- // 撤销, 应该会回到中速
- remoteControl.undoButtonWasPushed();
- // 这个时候开启高速
- remoteControl.onButtonWasPushed(1);
- System.out.println(remoteControl);
- // 再进行一次撤销, 应该会回到中速
- remoteControl.undoButtonWasPushed();
- }
- }
好了, 至此我们不仅仅实现了单个的开与关, 还实现了一整个遥控器所有控件的开与关, 甚至是复杂的家电的开与关(音响, 电扇的开启略复杂), 而且均实现了撤销. 作为程序员的你是不是经常使用撤销功能呢, 反正我是经常使用的噢.
但是, 这还不是终极状态. 我们在这里只能实现一个家电的开与关, 如果光凭按下一个按钮, 不能实现灯光, 电视, 音响的同步使用, 那这个遥控器对我们来说是不是还是有点 low 呢? 是吧, 确实有点 low, 如何破解, 敬请期待我们的下一篇.
爱生活, 爱学习, 爱感悟, 爱挨踢
来源: https://www.cnblogs.com/dimple91/p/10869038.html