《HeadFirst设计模式》第六章-命令模式

1.声明

设计模式中的设计思想、图片和部分代码参考自《Head First设计模式》作者Eric Freeman & Elisabeth Freeman & Kathy Siezza & Bert Bates。

在这里我只是对这本书进行学习阅读,并向大家分享一些心得体会。

2.需求

巴斯特家电自动化公司,主要的营业项目是向客户兜售自己的家电产品,并且这些家电都是受软件控制的,例如可以用软件打开客厅的电灯,但是最近巴斯特公司有了新的需求,新的需求是需要用一个遥控器来控制这些家电,例如,遥控器上可以控制客厅的灯的开启和关闭,并且将这个需求委托我们来做,作为回报,我们能够得到巴斯特公司的股票期权。

补充:巴斯特公司会给我们提供各个家电的API(API包括开启和关闭等功能)。

遥控器模型样例:

厂商提供的各个家电的API:

由API的接口可以看出,各个家电的接口并不统一,而且繁杂。

按照以往的设计思路,我们仍然希望遥控器和各个家电之间解耦,即遥控器不需要知道将要执行的是哪个家电,遥控器只管通知就可以了,所以一旦进行解耦,我们的代码中就不会出现大量的if else语句,因为我们知道,if else非常不利于维护吗,日后新增家电类型,我们还需要修改if else语句。

避免if-else,类似于:

public void excute(Command c) {
	if(c == LightOn) {
		//开灯
		light.on();
	}else if(c == LightOff) {
		//关灯
		light.off();
	}else if(c == FanOn) {
		//开风扇
		fan.off();
	}
}

3.命令模式

命令模式可将“动作的请求者”从”动作的执行者“对象中解耦。在这里请求者就是遥控器,执行者就是家电。

3.1借鉴餐厅的运作方式

我们在接触命令模式之前,先来研究一下餐厅的运作方式,或许我们能够得到一些启发。

餐厅的点餐流程:

以程序的角度描述餐厅的点餐流程:

流程描述:

把订单想象成一个用来准备点餐的对象,订单接口只包含一个方法,就是orderUP(),这个方法封装了准备点餐所需的动作。订单内有一个到"需要进行准备工作的对象”,即厨师的引用。所以女招待员不需要知道订单上有什么,也不需要知道谁来准备餐点,只需要将订单拿过来,然后放在订单窗口,然后大叫一声"订单来了"即可。

一天内,有不同的订单,这会使得招待员的takeOrder()方法传入不同的参数。女招待员知道所有的订单支持orderUP方法,需要烹饪餐点时,调用订单的orderUP()即可。

厨师具备烹饪餐点的只是,他知道如何准备餐点,一旦女招待员调用订单的orderUP(),厨师就开始烹饪。厨师和招待员之间是解耦的,二者不需要直接沟通,依赖于订单交流。

类比于家电系统:

遥控器的插槽是招待员,家电是厨师。

注:遥控器目前拥有七个可编程插槽,每个插槽都指向一个家用电器。

3.2程序实现

3.2.1命令模式样例

完成整个需求的代码肯定是较复杂所以我们先从简单的入手,先让遥控器能够控制电灯的开关。(注:这也是我在日常工作和学习中经常使用的方法,即设计一个系统,先用简单的方式跑通,然后再实现细节,就像汽车会先制造框架,素描画人时会先画轮廓一样)。

由于遥控器上的按钮会发出各种各样的命令,那么首先需要建立一个命令接口,来描述命令的特征。

电器-灯:

//家电-灯
public class Light {

	public Light() {
	}

	public void on() {
		System.out.println("开灯");
	}

	public void off() {
		System.out.println("关灯");
	}
}

命令接口:

public interface Command {

	//这个excute方法就是餐厅的订单的orderUP方法
	public void excute();
}

根据厂商提供的电灯的API,电灯一共有两个方法,分别是on()和off()。

开灯命令:

//开灯命令
public class LightOnCommand implements Command {
	
	//命令类中持有命令接收者对象的引用
	Light light;
  
	public LightOnCommand(Light light) {
		this.light = light;
	}
	
	//开灯,低层由Light去完成
	@Override
	public void execute() {
		light.on();
	}

}

仅支持开灯的遥控器(暂时使用):

/*
 * 命令的调用者:遥控器
 * 这里对遥控器的代码做了简化,只有一个按钮,为了测试开灯命令
 */
public class SimpleRemoteControl {
	
	//命令对象的引用
	Command slot;
 
	public SimpleRemoteControl() {}
 
	public void setCommand(Command command) {
		slot = command;
	}
 
	//按下按钮,即执行命令
	public void buttonWasPressed() {
		slot.execute();
	}
}

测试:

public class RemoteControlTest {
	public static void main(String[] args) {
		//创建遥控器对象
		SimpleRemoteControl remote = new SimpleRemoteControl();
		
		//创建家电-灯
		Light light = new Light();
		//创建开灯的命令
		LightOnCommand lightOn = new LightOnCommand(light);
 
		//遥控器设置此命令,并且执行命令
		remote.setCommand(lightOn);
		remote.buttonWasPressed();
    }
	
}

输出结果:

开灯

3.2.2命令模式的定义

命令模式

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

定义解析:

一个命令对象通过在特定接收者上绑定一组动作来封装一个请求。要达到这一点,命令对象将动作和接收者包进对象中。这个对象只暴露出一个execute方法,当此方法被调用的时候,接收者就会知道这些动作。从外面来看,其他对象不知道究竟哪个接收者进行了那些动作,只知道如果调用execute方法,请求的目的就达到了。

餐厅就是利用命令来参数化对象的一些例子。一整天下来,女招待员参数化许多订单。在简单遥控器中,我们先用一个"打开电灯"命令加载按钮插槽,同理我们也可以将命令替换成为另一个"打开车库门"这样的命令。就和女招待员一样,遥控器插槽根本不在乎所拥有的是什么命令,只要将命令对象实现了Command接口就可以了。

命令模式类图:

3.2.3程序的真正实现

实现遥控器:

实现遥控器之前,有两点需要注意一下:

  1. 每个电器都有最基本的开关按钮,那么电器的"开"命令和电器的"关"命令应该分开存储。
  2. 为了区分厨房和客厅的电灯,那么灯的开关命令下应该持有对应房间(厨房|客厅)的灯对象。

那么其实对于灯这个电器来说,它其实是有4中命令的(假设只有厨房和客厅有灯),厨房开灯命令,厨房关灯命令,客厅开灯命令,客厅关灯命令。

设计方案是,让遥控器的每个插槽对应一个命令。

===================================遥控器代码===================================

真正的遥控器代码:

//调用者,遥控器对象
public class RemoteControl {
	
	//装在七个插槽的"开"命令
	Command[] onCommands;
	//装在七个插槽的"关"命令
	Command[] offCommands;
 
	//初始化
	public RemoteControl() {
		onCommands = new Command[7];
		offCommands = new Command[7];
		//初始化时为所有按钮设置默认命令:noCommand
		Command noCommand = new NoCommand();
		for (int i = 0; i < 7; i++) {
			onCommands[i] = noCommand;
			offCommands[i] = noCommand;
		}
	}
  
	//设置命令      slot:插槽的位置[1,7]   onCommand:开命令        offCommand:关命令
	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();
	}
  
	//打印出插槽的位置和开关命令
	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();
	}
}

程序为没有设置命令的插槽赋予了空命令NoCommand,这样做避免了程序中的null判断。

===================================电器代码===================================

电器-灯:参考3.2.1代码

电器-吊扇:

//电器-吊扇
public class CeilingFan {
	String location = "";
	//吊扇转速级别
	int level;
	public static final int HIGH = 2; //高速
	public static final int MEDIUM = 1; //中速
	public static final int LOW = 0; //低速
 
	public CeilingFan(String location) {
		this.location = location;
	}
 
	public void high() {
		level = HIGH;
		System.out.println(location + " 设置转速为高速");
	} 

	public void medium() {
		level = MEDIUM;
		System.out.println(location + " 设置转速为中速");
	}

	public void low() {
		level = LOW;
		System.out.println(location + " 设置转速为低速");
	}
 
	public void off() {
		level = 0;
		System.out.println(location + " 关闭");
	}
 
	//获取吊扇转速
	public int getSpeed() {
		return level;
	}
}

电器-车库门:

// 电器-车库门
public class GarageDoor {
	String location;

	public GarageDoor(String location) {
		this.location = location;
	}

	public void up() {
		System.out.println(location + " 升起");
	}

	public void down() {
		System.out.println(location + " 降落");
	}

	public void stop() {
		System.out.println(location + " 停止");
	}

	public void lightOn() {
		System.out.println(location + " 开灯");
	}

	public void lightOff() {
		System.out.println(location + " 关灯");
	}
}

电器-音响:

//电器-音响
public class Stereo {
	
	//电器描述
	String location;

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

	public void on() {
		System.out.println(location + " 开启");
	}

	public void off() {
		System.out.println(location + " 关闭");
	}

	public void setCD() {
		System.out.println(location + " 放入CD");
	}

	public void setDVD() {
		System.out.println(location + " 放入DVD");
	}

	public void setRadio() {
		System.out.println(location + " 开启收音机");
	}

	public void setVolume(int volume) {
		System.out.println(location + " 将声音大小设置为: " + volume);
	}
}

===================================遥命令代码===================================

关灯命令:

//关灯命令
public class LightOffCommand implements Command {
	
	//命令接收者的引用
	Light light;
 
	public LightOffCommand(Light light) {
		this.light = light;
	}
 
	public void execute() {
		//底层由命令接收者去完成动作
		light.off();
	}
}

音响用CD开启命令:

//音响放入CD开启命令
public class StereoOnWithCDCommand implements Command {
	Stereo stereo;
 
	public StereoOnWithCDCommand(Stereo stereo) {
		this.stereo = stereo;
	}
 
	public void execute() {
		//音响开机
		stereo.on();
		//放入CD
		stereo.setCD();
		//设置音量为11
		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); //音响播放CD
		StereoOffCommand  stereoOff =
				new StereoOffCommand(stereo); //音响关闭
 
		//将命令设置到遥控器的相应插槽中
		remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff);
		remoteControl.setCommand(1, kitchenLightOn, kitchenLightOff);
		remoteControl.setCommand(2, ceilingFanOn, ceilingFanOff);
		remoteControl.setCommand(3, garageDoorUp, garageDoorDown);
		remoteControl.setCommand(4, 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);
		remoteControl.onButtonWasPushed(4);
		remoteControl.offButtonWasPushed(4);
	}
}

输出结果:

------ Remote Control -------
[slot 0] command.LightOnCommand    command.LightOffCommand
[slot 1] command.LightOnCommand    command.LightOffCommand
[slot 2] command.CeilingFanOnCommand    command.CeilingFanOffCommand
[slot 3] command.GarageDoorUpCommand    command.GarageDoorDownCommand
[slot 4] command.StereoOnWithCDCommand    command.StereoOffCommand
[slot 5] command.NoCommand    command.NoCommand
[slot 6] command.NoCommand    command.NoCommand

Living Room 开启
Living Room 关闭
Kitchen 开启
Kitchen 关闭
Living Room 设置转速为高速
Living Room 关闭
 升起
 升起
Living Room 开启
Living Room 放入CD
Living Room 将声音大小设置为: 11
Living Room 关闭

3.2.4撤销功能

撤销功能:

如果一开始客厅的灯是关闭的,那么当你按下开灯按钮,那么等会打开,这时,如果按下撤销按钮,那么会撤销最后一次对灯的操作,即撤销"开灯"这个操作,这时候灯就被关闭了。

修改命令接口,定义撤销方法:

public interface Command {

	//这个execute方法就是餐厅的订单的orderUP方法
	public void execute();
	
	//3.2.4新增:撤销最近一次的操作
	public void undo();
}

修改开灯命令:

//开灯命令
public class LightOnCommand implements Command {
	
	//命令类中持有命令接收者对象的引用
	Light light;
  
	public LightOnCommand(Light light) {
		this.light = light;
	}
	
	//开灯,低层由Light去完成
	@Override
	public void execute() {
		light.on();
	}

	//3.2.4新增:撤销开灯命令
	@Override
	public void undo() {
		light.off();
	}

}

修改关灯命令:

//关灯命令
public class LightOffCommand implements Command {
	
	//命令接收者的引用
	Light light;
 
	public LightOffCommand(Light light) {
		this.light = light;
	}
 
	public void execute() {
		//底层由命令接收者去完成动作
		light.off();
	}

	//3.2.4新增撤销关灯命令
	@Override
	public void undo() {
		light.on();
	}
}

对于遥控器来说,想要撤销最后一步的命令,那么最起码应该知道遥控器的最后一次命令是哪个,所以我们需要新增一个变量来记录最后一次的命令。

遥控器代码:

//调用者,遥控器对象
public class RemoteControl {
	
	//装在七个插槽的"开"命令
	Command[] onCommands;
	//装在七个插槽的"关"命令
	Command[] offCommands;
	//3.2.4新增:记录最后一次操作的命令
	Command undoCommand;
 
	//初始化
	public RemoteControl() {
		onCommands = new Command[7];
		offCommands = new Command[7];
		//初始化时为所有按钮设置默认命令:noCommand
		Command noCommand = new NoCommand();
		for (int i = 0; i < 7; i++) {
			onCommands[i] = noCommand;
			offCommands[i] = noCommand;
		}
		//3.2.4新增:初始化最后一次的命令,开始时无命令,赋值为空对象
		undoCommand = noCommand;
	}
  
	//设置命令      slot:插槽的位置[1,7]   onCommand:开命令        offCommand:关命令
	public void setCommand(int slot, Command onCommand, Command offCommand) {
		//设置开关命令在相应位置
		onCommands[slot] = onCommand;
		offCommands[slot] = offCommand;
	}
 
	//对应插槽位置的开命令调用
	public void onButtonWasPushed(int slot) {
		onCommands[slot].execute();
		//3.2.4新增:更新undoCommand
		undoCommand = onCommands[slot];
	}
	//对应插槽位置的关命令调用
	public void offButtonWasPushed(int slot) {
		offCommands[slot].execute();
		//3.2.4新增:更新undoCommand
		undoCommand = offCommands[slot];
	}
	
	//3.2.4新增:当撤销按钮被按下时,这时候调用命令的撤销方法
	public void undoButtonWasPushed() {
		undoCommand.undo();
	}
  
	//打印出插槽的位置和开关命令
	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();
	}
}

撤销-电灯部分的测试代码:

public class RemoteLoader {
 
	public static void main(String[] args) {
		RemoteControl remoteControl = new RemoteControl();
 
		Light livingRoomLight = new Light("Living Room");
 
		LightOnCommand livingRoomLightOn = 
				new LightOnCommand(livingRoomLight);
		LightOffCommand livingRoomLightOff = 
				new LightOffCommand(livingRoomLight);
 
		remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff);
 
		remoteControl.onButtonWasPushed(0);
		remoteControl.offButtonWasPushed(0);
		System.out.println(remoteControl);
		remoteControl.undoButtonWasPushed();
		remoteControl.offButtonWasPushed(0);
		remoteControl.onButtonWasPushed(0);
		System.out.println(remoteControl);
		remoteControl.undoButtonWasPushed();
	}
}

输出结果:

Living Room 开启
Living Room 关闭

------ Remote Control -------
[slot 0] command.LightOnCommand    command.LightOffCommand
[slot 1] command.NoCommand    command.NoCommand
[slot 2] command.NoCommand    command.NoCommand
[slot 3] command.NoCommand    command.NoCommand
[slot 4] command.NoCommand    command.NoCommand
[slot 5] command.NoCommand    command.NoCommand
[slot 6] command.NoCommand    command.NoCommand

Living Room 开启
Living Room 关闭
Living Room 开启

------ Remote Control -------
[slot 0] command.LightOnCommand    command.LightOffCommand
[slot 1] command.NoCommand    command.NoCommand
[slot 2] command.NoCommand    command.NoCommand
[slot 3] command.NoCommand    command.NoCommand
[slot 4] command.NoCommand    command.NoCommand
[slot 5] command.NoCommand    command.NoCommand
[slot 6] command.NoCommand    command.NoCommand

Living Room 关闭

下面看看吊扇这种多状态的命令如何实现撤销。

家电-吊扇:

//电器-吊扇
public class CeilingFan {
	String location = "";
	//吊扇转速级别
	int level;
	public static final int HIGH = 3; //高速
	public static final int MEDIUM = 2; //中速
	public static final int LOW = 1; //低俗
	public static final int OFF = 0;
 
	public CeilingFan(String location) {
		this.location = location;
	}
 
	public void high() {
		level = HIGH;
		System.out.println(location + " 设置转速为高速");
	} 

	public void medium() {
		level = MEDIUM;
		System.out.println(location + " 设置转速为中速");
	}

	public void low() {
		level = LOW;
		System.out.println(location + " 设置转速为低速");
	}
 
	public void off() {
		level = OFF;
		System.out.println(location + " 关闭");
	}
 
	//获取吊扇转速
	public int getSpeed() {
		return level;
	}
}

吊扇高速转动命令:

//吊扇高速转动命令
public class CeilingFanHighCommand implements Command {
	CeilingFan ceilingFan;
	//记录本次高速命令之前的命令
	int prevSpeed;
  
	public CeilingFanHighCommand(CeilingFan ceilingFan) {
		this.ceilingFan = ceilingFan;
	}
 
	public void execute() {
		prevSpeed = ceilingFan.getSpeed();
		ceilingFan.high();
	}
 
	//判断最后一次操作的命令prevSpeed是什么,从而回滚到之前的速度
	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();
		}
	}
}

吊扇中速转动命令:

//吊扇中速转动命令
public class CeilingFanMediumCommand implements Command {
	CeilingFan ceilingFan;
	int prevSpeed;
  
	public CeilingFanMediumCommand(CeilingFan ceilingFan) {
		this.ceilingFan = ceilingFan;
	}
 
	public void execute() {
		prevSpeed = ceilingFan.getSpeed();
		ceilingFan.medium();
	}
 
	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();
		}
	}
}

吊扇关闭转动的命令:

public class CeilingFanOffCommand implements Command {
	CeilingFan ceilingFan;
	int prevSpeed;
  
	public CeilingFanOffCommand(CeilingFan ceilingFan) {
		this.ceilingFan = ceilingFan;
	}
 
	public void execute() {
		prevSpeed = ceilingFan.getSpeed();
		ceilingFan.off();
	}
 
	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();
		}
	}
}

测试代码:

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

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

输出结果:

Living Room 设置转速为中速
Living Room 关闭

------ Remote Control -------
[slot 0] command.CeilingFanMediumCommand    command.CeilingFanOffCommand
[slot 1] command.CeilingFanHighCommand    command.CeilingFanOffCommand
[slot 2] command.NoCommand    command.NoCommand
[slot 3] command.NoCommand    command.NoCommand
[slot 4] command.NoCommand    command.NoCommand
[slot 5] command.NoCommand    command.NoCommand
[slot 6] command.NoCommand    command.NoCommand

Living Room 设置转速为中速
Living Room 设置转速为高速

------ Remote Control -------
[slot 0] command.CeilingFanMediumCommand    command.CeilingFanOffCommand
[slot 1] command.CeilingFanHighCommand    command.CeilingFanOffCommand
[slot 2] command.NoCommand    command.NoCommand
[slot 3] command.NoCommand    command.NoCommand
[slot 4] command.NoCommand    command.NoCommand
[slot 5] command.NoCommand    command.NoCommand
[slot 6] command.NoCommand    command.NoCommand

Living Room 设置转速为中速

3.2.5Party模式

由于一个一个的操控电器太麻烦,现在我们需要一个party模式,就是一键就可以让各个电器达到我们要求的状态,例如:客厅灯打开、客厅吊扇打开、电视打开等这一系列操作一步完成,并且在现有架构就能完成,那么如何才能实现呢?

首先我们知道,之前的命令类,都只能执行一种动作,那么如果我们定义一个命令类,该类可以一步(excute)执行多种动作,那么party模式是不是就能实现了呢?

定义宏命令类:

//宏命令类
public class MacroCommand implements Command {
	
	//执行的一组命令
	Command[] commands;
 
	public MacroCommand(Command[] commands) {
		this.commands = commands;
	}
 
	//遍历命令数组依次执行
	public void execute() {
		for (int i = 0; i < commands.length; i++) {
			commands[i].execute();
		}
	}
 
	//撤销功能,但是必须倒序撤销
	public void undo() {
		for (int i = commands.length -1; i >= 0; i--) {
			commands[i].undo();
		}
	}
}

线面我们用宏命令实现这样的功能,按下开启按钮时,打开客厅的灯,并将客厅吊扇设置为高速;按下关闭按钮时,将客厅灯设置为关闭,并且将客厅吊扇设置为中速(注:这里就不设计复杂的party命令了,因为思想相同)。

测试party模式:

//party命令测试类
public class RemoteLoader {

	public static void main(String[] args) {

		//初始化遥控器
		RemoteControl remoteControl = new RemoteControl();

		Light light = new Light("Living Room");
		CeilingFan ceilingFan = new CeilingFan("Living Room");
 
		LightOnCommand lightOn = new LightOnCommand(light);
		CeilingFanHighCommand ceilingFanHigh = new CeilingFanHighCommand(ceilingFan);
		
		LightOffCommand lightOff = new LightOffCommand(light);
		CeilingFanMediumCommand ceilingFanMedium = new CeilingFanMediumCommand(ceilingFan);
		
		// partyOn:开灯并使吊扇高速转动
		Command[] partyOn = { lightOn, ceilingFanHigh};
		// partyOff:关灯并使吊扇中速转动
		Command[] partyOff = { lightOff, ceilingFanMedium};
  
		//初始化MacroCommand,以命令数组作为参数
		MacroCommand partyOnMacro = new MacroCommand(partyOn);
		MacroCommand partyOffMacro = new MacroCommand(partyOff);
 
		//在遥控器的第一个按钮处设置party命令
		remoteControl.setCommand(0, partyOnMacro, partyOffMacro);
  
		System.out.println(remoteControl);
		System.out.println("--- Pushing Macro On---");
		remoteControl.onButtonWasPushed(0);
		System.out.println("--- Pushing Macro Off---");
		remoteControl.offButtonWasPushed(0);
	}
}

输出结果:

------ Remote Control -------
[slot 0] command.MacroCommand    command.MacroCommand
[slot 1] command.NoCommand    command.NoCommand
[slot 2] command.NoCommand    command.NoCommand
[slot 3] command.NoCommand    command.NoCommand
[slot 4] command.NoCommand    command.NoCommand
[slot 5] command.NoCommand    command.NoCommand
[slot 6] command.NoCommand    command.NoCommand

--- Pushing Macro On---
Living Room 开启
Living Room 设置转速为高速
--- Pushing Macro Off---
Living Room 关闭
Living Room 设置转速为中速

4.总结

4.1补充说明

  • 之所以不讲接收者的处理逻辑写在调用者中,原因是:分开写更符合解耦的原则。
  • 宏命令初始化时,传入的参数是命令数组,命令数组存在的意义是,可以动态的订制宏命令。

4.2命令模式的其他用途

4.2.1队列请求

4.2.2日志请求

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

琴瘦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值