设计模式六之命令模式(java)

这是我看Head first设计模式书籍之后想要总结的知识点,一方面是对自己学习的东西总结和提炼加强自己的理解和记忆,另一方面是给大家简化这本书,方便大家快速了解各种设计模式。

我想提醒大家的是,设计模式只是前人总结的一些经验套路,实际上还是要在开发项目中慢慢体会,不可成为设计模式的中毒患者,强行照搬设计模式的一些规则。


这次,我们把方法调用封装起来,调用此运算的对象不需要关心事情是如何进行的,只要知道如何使用包装成形

的方法来完成它就可以。通过封装方法调用,可以用来记录日志;或者重复使用这些封装来实现撤销(undo).

这次我们要讲解的设计模式是命令模式。

我们举个栗子:

我们需要创建一组控制遥控器的API,遥控器的每个插槽都能够控制一个或一组装置。请注意,能够控制目前的装置和任何

未来可能出现的装置,这一点是很重要的。

遥控器有七个插槽,可以安装不同的设备,然后可以使用遥控器上的每个插槽对应的开关按钮控制它。

最后遥控器上有一个撤销的命令,可以撤销最后一次操作,回到倒数第二次操作。


思路:

我们遥控器不需要知道各种设备的具体实现细节,只需要知道按钮被按下发出正确的请求。

因此需要想办法将"动作的请求者"(遥控器)从"动作的执行者"(各种装置)中解耦.

在设计中采用"命令对象"就可以办到。利用命令对象,把请求(电灯开灯)封装成一个特定对象。当按钮被按下的时候,

就可以请命令对象做相关的工作,遥控器并不需要知道工作内容是什么.这样就可以实现遥控器和装置设备解耦。


直接看代码部分:

首先,让所有的命令行对象实现相同的包含一个方法的接口。

//让所有的命令对象实现相同的包含一个方法的接口
public interface Command{

	public void execute(); 
}

实现一个打开电灯的命令 假设厂商提供的类Light类有两个方法:on()和off()

//实现一个打开电灯的命令
public class LightOnCommand implements Command{

	Light light;

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

	public void execute(){
		light.on();
	}
}
//实现一个关闭电灯的命令
public class LightOffCommand implements Command{

	Light light;

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

	public void execute(){
		light.off();
	}
}

假设我们有一个遥控器,它只有一个按钮和对应的插槽,可以控制一个装置

//使用命令对象
public class SimpleRemoteControl{
	//有一个插槽持有命令,而这个命令控制着一个装置
	Command slot;

	public SimpleRemoteControl(){}

	public vyoid setCommand(Command command){
		slot = command;
	}

	public void buttonWasPressed(){
		slot.execute();
	}
}

简单测试

//测试代码
public class RemoteControllTest{

	public static void main(String args[]){
		SimpleRemoteControl remote = new SimpleRemoteControl();
		Light light = new Light();
		LightOnCommand lightOn = new LightOnCommand(light);
		remote.setCommand(lightOn);
		remote.buttonWasPressed();
	}
}


下面是我们的命令模式出厂了:

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

命令对象通过在特定接受者上绑定一组动作来封装一个请求。它只暴露出一个execute()方法,当此方法被调用

的时候,接受者就会进行这些动作。从外面来看,其他对象不知道究竟哪个接受者进行了哪些动作,只知道如果调用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 < onCommands.length; i++){
			//预先初始化,赋值为NoCommand类型对象,execute()方法是空的,也就表示没有插入装置
			onCommands[i] = noCommand;
			offCommands[i] = noCommand;
		}
	}

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

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

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

测试代码:

//测试代码
/*假设我们厂商已经提供了Light CeilingFan GarageDoor Stereo这些设备类*/                   
public  class RemoteLoader{
	public void main(String args[]){

		RemoteControl remoteControl = new RemoteControl();

		Light livingRoomLight = new Ligh("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(libingRoomLight);

		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 stereOnWithCD = new StereoOnWithCDCommand(stereo);
		StereoOffCommand stereOff = 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);
	}
}

为了实现撤销的功能 我们需要在Command接口中加入undo()抽象方法,并且在各种实现子类中实现undo()方法

public interface Command{
	public void execute();
	public void undo();
}

public class LightOnCommand implements Command{

	Light light;

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

	public void execute(){
		light.on();
	}

	public void undo(){
		light.off();
	}
}

//修改遥控器的类
public class RemoteControlWithUndo{
	Command[] onCommands;
	Command[] offCommands;
	//前一个命令将被记录在这里
	Command undoCommand;

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

		Command noCommand = new NoCommand();
		for(int i = 0; i < onCommands.length; i++){
			//预先初始化,赋值为NoCommand类型对象,execute()方法是空的,也就表示没有插入装置
			onCommands[i] = noCommand;
			offCommands[i] = noCommand;
		}
		undoCommand = noCommand;
	}

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

	public void onButtonWasPressed(int slot){
		onCommands[slot].execute();
		undoCommand = onCommands[slot];
	}

	public void onButtonWasPushed(int slot){
		offCommands[slot].execute();
		undoCommand = offCommands[slot];
	}

	//加入撤销命令的方法
	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 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(); //每个命令对象都实现了execute()方法
		}
	}
}

命令模式还可以用于队列请求(线程从队列中取到命令对象,然后调用命令对象的execute()方法),或者日志请求(在命令接口Command加入store()和load()接口,每当命令执行时,我们就将执行内容存储在磁盘中,如果要撤销,只需要加载以及重新撤销操作即可)。具体的还是看看其他的文章。

下一次我们将要讲适配器模式和外观模式,这个用的可是很多的啊。


猜你喜欢

转载自blog.csdn.net/qq_32252957/article/details/80586044