设计模式之命令模式

    命令模式,从字面义来看就好像是上级对下级下达命令,上级只管通知下级,但是不会知道下级是如何完成的。假设一个场景:某个公司,想知道实现全自动家庭,其有很多合作商,好比电视机厂商,油烟机厂商等,他们的产品的代码的接口都是不同的,然而你作为一个CTO,你的CEO期望你能够将这些东西完整的组织起来,真正的完成这项工作,开拓出新的市场。

      在这里正好有一种模式可以提供一种新的思路或者是方法----命令模式,如果觉得上述的文字太过于简洁,我们可以参考下面的例子:

      这个例子模拟了对灯的控制,灯这个接口仅仅定义了on和off两个方法来控制灯的状态,所以我们基于讨论的命令模式来写一个对灯的控制的例子:

      首先定义一个Command类的接口,里面定义了需要执行的方法:

public interface Command{
	public void execute();
}
其次,我们需要构建一个LightOnCommand类继承这个接口,并实现了一个灯打开的类:

public class LightOnCommand implements Command{
	Light light;
	public LightOnCommand(Light light){
		this.light = light;
	}
	public void execute(){
		light.on();
	}
}
     可以看到,light对象的on方法被封装到execute方法里面,execute方法则有LIghtOnCommand方法调用;

     之后构建一个SimpleRemoteControl类来调用执行命令,即执行execute,通过setCommand方法将LightOnCommand对象传入,通过buttonWasPressed来模拟按钮按下的过程,这样就可以比较完整的模拟了电灯被按钮控制的过程。

     下面开始进行测试:

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

测试结果如下:



     是不是对这个新的模式有了一点认识了呢,让我们规范的叙述一下,什么是命令模式:将请求封装成对象,以便使用不同的请求、队列或者是日志来参数化其他对象,命令模式也支持可撤销的操作。通过结合上面的例子我们能够了解到,一个命令对象通过在特定接收者上绑定一组动作来封装一个请求,要达到这一点,命令对象将动作和接收者包进对象之中,这个对象仅暴露一个execute方法,当此方法被调用的时候接收者就会进行这些动作。从外面来看,其他对象不知道究竟那个接收者进行了那些动作,只知道如果调用execute方法,请求就能够达到目的。结合一个类图来梳理一下:



       假设我们按照上面的方式,将其它的灯也这样组织起来,我们暂定为有7个控制按钮,接下来实现这个远程操纵的遥控器:

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 offCommad){
		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 " + +"] " + onCommands[i].getClass().getName()
				+ "   " + offCommands[i].getClass.getName() + "\n");

		}
	}

	return stringBuff.toString();
}

接着,对于命令的构造,延续上面的Command接口,以及LightOnCommand类,下面实现命令LightOffCommand类:

public class LightOffCommand implements Command{
	Light light;

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

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

     到这里我们再添加一个音响类吧,这样的话,可以模拟关灯,听音乐的场景,有点无厘头哈,为了能够说明白这个模式,那就将就着吧,哈哈,或者添加到了其它的场所,下面设计这个类:

public class StereoOnWithCDCommand implements Command{
	Stereo stereo;

	public StereoOnWithCDCommand(Stereo steroe){
		this.stereo = stereo;
	}

	public void execute(){
		stereo.on();
		stereo.setCD();
		stereo.setVolume(11);
	}
}
让我们来测试这些命令吧:

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

		//将所有的装置创建在适合的位置
		Light livingRoomLight = new Light("living Room");
		Light kitchenLight = new Light("Kitchen");
		CellingFan 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 ketchenLightOn = new LightOnCommand(kitchenLight);
		LightOffCommand ketchenLightOff = 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,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);
	}
}
    在上面的代码中你可能会发现里面有一个NoCommand类,是的,这个是用作再RemoteControl构造器中,将每个插槽都预先置顶成为NoCommand对象,以确定每个插槽永远都有命令对象。所以在测试中,没有明确指出的指令命令的插槽都是默认的NoCommand对象。上述的过程能够帮助我们实现正向的打开电灯等的过程,那么我们如何设计一个逆向的撤销方法呢?

     我们在上面的代码中定义undo方法,也就是说我们在原来的Command接口中加入undo方法的定义:

public interface Command{
	public void execute();
	public void undo();
}
接着,利用电灯的例子实现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();
	}
}
然后在LightOffCommand类中实现undo方法,其实就是调用on()方法:

public class LightOffCommand implements Command{
	Light light;

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

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

	public void undo(){
		light.on();
	}
}
    仅仅在原有类中实现了好像还是不可以,因为不知道怎么去追踪控制器按下的是哪一个按钮,在这里新设计了一个类,RemoteControlWithUndo类:

public class ReomteControlWithUndo{
	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 < 7 ; i++ ){
			onCommands[i] = noCommand;
			offCommands[i] = noCommand;
		}

		undoCommand = noCommand;
	}

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

	}

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

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

	public void undoButtonWasPushed(int slot){
		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 " + +"] " + onCommands[i].getClass().getName()
				+ "   " + offCommands[i].getClass.getName() + "\n");

		}

}

为了测试这个功能,我们在刚才设计的RemoteLoader类的主函数中添加下面的内容(仅列出了需要增加或者是修改的内容):

public class RemoteLoader{
	public static void mian(String[] args){
		RemoteCOntrolWithUndo remoteControl = new RemoteControlWithUndo();

		//将所有的装置创建在适合的位置
		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();
		}
}
      说到这里,要说两个小问题,第一,我们能不能实现多层次的撤销操作,换句话说能不能撤销到较早的状态。可以的,不过要记录使用过的命令,而不是和上面一样仅仅记录一个命令了,将这些命令压倒栈中,当执行撤销命令时,可以从栈中找到上面的命令,然后调用它的undo方法。

      第二个问题是,如何能够更好的通过按下一个按钮同时处理灯光等其它物件的控制呢,我们可以通过构建一个综合的命令,宏命令,也就是说将所有的包含on的命令集合起来统一管理,将所有的off命令统一起来,之后,放置到固定的数组或者其他容器中,之后开始调用。命令模式就先介绍到这里,之后在进行补充。

    









 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值