java 开发模式之六 : 命令模式

原理或定义

命令模式将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

命令模式的本质是对命令进行封装,将发出命令的责任和执行命令的责任分割开,实现二者之间的松耦合。

结构

Command: 定义命令的接口,声明执行的方法。
ConcreteCommand: 命令接口实现对象,通常会持有接受者,并调用接受者的功能来完成命令要执行的操作。
Receiver: 接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求的相应功能。
Invoker: 要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,相当于使用命令对象的入口。
Client: 创建具体的命令对象,并且设置命令对象的接收者。

类图


案例与代码

本案例使用家电自动化遥控器API项目来作为示例


通过一个遥控器同时控制一个或多个电灯和音响。


一般设计方案:

public interface Control {

	public void onButton(int slot);

	public void offButton(int slot);
	
	public void undoButton();
}
public class TraditionControl implements Control {
	Light light;
	Stereo stereo;


	public TraditionControl(Light light, Stereo stereo) {
		this.light = light;
		this.stereo = stereo;
	}


	@Override
	public void onButton(int slot) {
		// TODO Auto-generated method stub
		switch (slot) {
		case 0:
			light.On();
			break;
		case 1:
			stereo.On();
			break;
		case 2:
			int vol = stereo.GetVol();
			if (vol < 11) {
				stereo.SetVol(++vol);
			}
			break;
		}
	}


	@Override
	public void offButton(int slot) {
		// TODO Auto-generated method stub
		switch (slot) {
		case 0:
			light.Off();
			break;
		case 1:
			stereo.Off();
			break;
		case 2:
			int vol = stereo.GetVol();
			if (vol > 0) {
				stereo.SetVol(--vol);
			}
			break;
		}
	}


	@Override
	public void undoButton() {
		// TODO Auto-generated method stub
		
	}


	


}
public class ControlTest {
	public static void main(String[] args) {
		Control ctl;
		Light light = new Light("Bedroom");
		Stereo stereo = new Stereo();
		ctl = new TraditionControl(light, stereo);


		ctl.onButton(0);
		ctl.offButton(0);
		ctl.onButton(1);
		ctl.onButton(2);
		ctl.offButton(2);


		ctl.offButton(1);
	}
}

每次扩展设备和添加按钮都会修改以上的类。


命令模式设计方案:

类图:



Command接口:

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

ConcreteCommand 实现类:

public class LightOffCommand implements Command {
	private Light light;
	public LightOffCommand(Light light)
	{
		this.light=light;
	}
	@Override
	public void execute() {
		// TODO Auto-generated method stub
		light.Off();
	}

	@Override
	public void undo() {
		// TODO Auto-generated method stub
		light.On();
	}

}
public class LightOnCommand implements Command {
	private Light light;
	
	public LightOnCommand(Light light)
	{
		this.light=light;
		
	}
	@Override
	public void execute() {
		// TODO Auto-generated method stub
		light.On();
	}


	@Override
	public void undo() {
		// TODO Auto-generated method stub
		light.Off();
	}


}
public class StereoAddVolCommand implements Command {
	private Stereo setreo;
	public StereoAddVolCommand(Stereo setreo)
	{
		this.setreo=setreo;
	}
	
	@Override
	public void execute() {
		// TODO Auto-generated method stub
	int vol=	setreo.GetVol();
	if(vol<11)
	{
		setreo.SetVol(++vol);
	}
		
	}


	@Override
	public void undo() {
		// TODO Auto-generated method stub
	int vol=	setreo.GetVol();
	if(vol>0)
	{
		setreo.SetVol(--vol);
	}
		
	}


}
public class StereoOffCommand implements Command {
	private Stereo setreo;
	public StereoOffCommand(Stereo setreo)
	{
		this.setreo=setreo;
	}
	
	@Override
	public void execute() {
		// TODO Auto-generated method stub
		setreo.Off();
	}


	@Override
	public void undo() {
		// TODO Auto-generated method stub
		setreo.On();
		setreo.SetCd();
	}


}
public class StereoOnCommand implements Command {
	private Stereo setreo;
	public StereoOnCommand(Stereo setreo)
	{
		this.setreo=setreo;
	}
	
	@Override
	public void execute() {
		// TODO Auto-generated method stub
		setreo.On();
		setreo.SetCd();
		
	}


	@Override
	public void undo() {
		// TODO Auto-generated method stub
		setreo.Off();
	}


}
public class StereoSubVolCommand implements Command {
	private Stereo setreo;
	public StereoSubVolCommand(Stereo setreo)
	{
		this.setreo=setreo;
	}
	
	@Override
	public void execute() {
		// TODO Auto-generated method stub
	int vol=	setreo.GetVol();
	if(vol>0)
	{
		setreo.SetVol(--vol);
	}
		
	}


	@Override
	public void undo() {
		// TODO Auto-generated method stub
	int vol=	setreo.GetVol();
	if(vol<11)
	{
		setreo.SetVol(++vol);
	}
		
	}


}
public class NoCommand implements Command {


	@Override
	public void execute() {
		// TODO Auto-generated method stub


	}
	@Override
	public void undo() {
		// TODO Auto-generated method stub


	}
}


Receiver(设备类):

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

}
public class Stereo {
	static int volume = 0;


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


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


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


	public void SetVol(int vol) {
		volume = vol;
		System.out.println("Stereo volume=" + volume);
	}


	public int GetVol() {
		return volume;
	}


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

Invoker:

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() {
		// TODO Auto-generated method stub
		stack.pop().undo();
	}

}

Client:

public class ControlTest {

	public static void main(String[] args) {
		CommandModeControl control = new CommandModeControl();
		MarcoCommand onmarco,offmarco;
		Light bedroomlight = new Light("BedRoom");
		Light kitchlight = new Light("Kitch");
		Stereo stereo = new Stereo();
		
		
		
		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};
			
		onmarco=new MarcoCommand(oncommands);
		offmarco=new MarcoCommand(offcommands);
		
		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.offButton(0);
		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);
	}

}




使用场景

1.系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。
2.系统需要在不同的时间指定请求,将请求排队和执行请求。
3.系统需要支持命令的撤销和恢复操作。
4.系统需要将一组操作组合在一起,即支持宏命

优缺点

主要优点有:

1.降低系统的耦合度
2.新的命令可以很容易地加入到系统中
3.可以比较容易地设计一个组合命令

 

缺点主要有

使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个命令都需要设计一个具体命令类,因此某些系统可能需要大量具体命令类,这将影响命令模式的使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章