设计模式之命令模式Command Pattern-Java版

一、定义

The Command Pattern encapsulates a request as an
object, thereby letting you parameterize other objects
with different requests, queue or log requests, and support
undoable operations.
命令模式把请求封装为一个对象,因此,你可以使用不同的请求、队列或日志请求
参数化其他对象,并支持可撤消的操作。

从定义中,可以得知命令模式的核心在于把请求封装为对象,它的应用主要在处理请求队列、日志请求、事务处理等方面。

二、实例

一间房子有三种电器可以通过遥控来控制:电灯、音响、车库门。如何实现远程遥控控制?同时支持撤销操作?
(一)直观思路:三个实体类电灯、音响、车库门,和一个控制类,控制类有三个类的对象引用。这种思路太古板了,勉强能实现功能,但是它的可扩展性很低,如果再增加一个电器或删除一个电器,代码就得大幅度删改,不符合开闭原则。也不能满足撤销功能。

class Light {
	public void on() {
		System.out.println("开灯");
	}
	
	public void off() {
		System.out.println("关灯");
	}
}

class Stereo {
	public void on() {
		System.out.println("开音响");
	}
	
	public void off() {
		System.out.println("关音响");
	}
}

class GarageDoor {
	public void on() {
		System.out.println("开车库门");
	}
	
	public void off() {
		System.out.println("关车库门");
	}
}

public class RemoteControl {
	private Light light;
	private Stereo stereo;
	private GarageDoor door;
	public RemoteControl(Light light, Stereo stereo, GarageDoor door) {
		this.light = light;
		this.stereo = stereo;
		this.door = door;
	}
	
	public void lightOn() {
		light.on();
	}
	
	public void lightOff() {
		light.off();
	}
	
	public void stereoOn() {
		stereo.on();
	}
	
	public void stereoOff() {
		stereo.off();
	}
	
	public void doorOn() {
		door.on();
	}
	
	public void doorOff() {
		door.off();
	}
	
	public static void main(String[] args) {
		RemoteControl rc=new RemoteControl(new Light(), new Stereo(), new GarageDoor());
		rc.lightOn();
		rc.stereoOn();
		rc.doorOn();
		
		rc.lightOff();
		rc.stereoOff();
		rc.doorOff();
	}
}

(二)根据多态的性质,我们可以把每种设备的行为封装起来,做成一个接口。同时,为了降低对设备的依赖性,考虑到设备的打开与关闭是两个操作,为每种设备创建一个打开操作类和关闭操作类,都实现公有接口,同时持有对应设备的引用,控制类只需要拥有公有接口类型的引用即可。

public interface Command {
	public void execute();
	
	public void undo();
	
	public static void main(String[] args) {
		SimpleRemoteControl remote=new SimpleRemoteControl();
		Light light=new Light();
		Stereo stereo=new Stereo();
		GarageDoor garageDoor=new GarageDoor();
		
		LightOnCommand lightOn=new LightOnCommand(light);
		LightOffCommand lightOff=new LightOffCommand(light);
		StereoOnCommand stereoOn=new StereoOnCommand(stereo);
		StereoOffCommand stereoOff=new StereoOffCommand(stereo);
		GarageDoorOpenCommand garageOpen=new GarageDoorOpenCommand(garageDoor);
		GarageDoorCloseCommand garageClose=new GarageDoorCloseCommand(garageDoor);
	
		remote.setCommand(0, lightOn, lightOff);
		remote.setCommand(1, stereoOn, stereoOff);
		remote.setCommand(2, garageOpen, garageClose);
		
		System.out.println(remote);
		
		remote.onButtonPressed(0);
		remote.onButtonPressed(1);
		remote.onButtonPressed(2);
		remote.offButtonPressed(0);
		remote.undo();
		remote.offButtonPressed(1);
		remote.offButtonPressed(2);
		remote.undo();
	}
}
//什么都不做
class NoCommand implements Command {

	@Override
	public void execute() {	}

	@Override
	public void undo() {}
	
}

class Light {
	public void on() {
		System.out.println("灯->开灯");
	}
	
	public void off() {
		System.out.println("灯->关灯");
	}
}

class LightOnCommand implements Command {
	private Light light;
	
	public LightOnCommand(Light light) {
		this.light = light;
	}

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

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

class LightOffCommand implements Command {
	private Light light;
	
	public LightOffCommand(Light light) {
		this.light = light;
	}

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

	@Override
	public void undo() {
		light.on();
	}
}

class Stereo {
	public void on() {
		System.out.println("立体声音响->打开");
	}
	
	public void off() {
		System.out.println("立体声音响->关闭");
	}
	
}

class StereoOnCommand implements Command {
	private Stereo stereo;
	public StereoOnCommand(Stereo stereo) {
		this.stereo=stereo;
	}
	
	@Override
	public void execute() {
		stereo.on();
	}

	@Override
	public void undo() {
		stereo.off();
	}
}

class StereoOffCommand implements Command {
	private Stereo stereo;
	public StereoOffCommand(Stereo stereo) {
		this.stereo=stereo;
	}
	
	@Override
	public void execute() {
		stereo.off();
	}

	@Override
	public void undo() {
		stereo.on();
	}
}

class GarageDoor {
	public void open() {
		System.out.println("门->开门");
	}
	
	public void close() {
		System.out.println("门->关门");
	}
}

class GarageDoorOpenCommand implements Command {
	private GarageDoor gd;
	public GarageDoorOpenCommand(GarageDoor gd) {
		this.gd = gd;
	}
	
	@Override
	public void execute() {
		gd.open();
	}

	@Override
	public void undo() {
		gd.close();
	}
}

class GarageDoorCloseCommand implements Command {
	private GarageDoor gd;
	public GarageDoorCloseCommand(GarageDoor gd) {
		this.gd = gd;
	}

	@Override
	public void execute() {
		gd.close();
	}

	@Override
	public void undo() {
		gd.open();
	}
}

class SimpleRemoteControl {
	private Command[] onCommands;
	private Command[] offCommands;
	private Command undoCommand;
	
	public SimpleRemoteControl() {
		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 offCommand) {
		onCommands[slot]=onCommand;
		offCommands[slot]=offCommand;
	}
	
	public void onButtonPressed(int slot) {
		onCommands[slot].execute();
		undoCommand=onCommands[slot];
	}
	
	public void offButtonPressed(int slot) {
		offCommands[slot].execute();
		undoCommand=offCommands[slot];
	}
	
	public void undo() {
		undoCommand.undo();
	}
	
	public String toString() {
		StringBuffer string=new StringBuffer();
		string.append("\n------ Remote Control ------\n");
		for(int i=0;i<onCommands.length;i++) {
			string.append("[slot "+i+"] "+onCommands[i].getClass().getName()+"    "
					+offCommands[i].getClass().getName()+"\n");
		}
		return string.toString();
	}
}

运行结果:


------ Remote Control ------
[slot 0] chapter6.command.demo2.LightOnCommand    chapter6.command.demo2.LightOffCommand
[slot 1] chapter6.command.demo2.StereoOnCommand    chapter6.command.demo2.StereoOffCommand
[slot 2] chapter6.command.demo2.GarageDoorOpenCommand    chapter6.command.demo2.GarageDoorCloseCommand
[slot 3] chapter6.command.demo2.NoCommand    chapter6.command.demo2.NoCommand
[slot 4] chapter6.command.demo2.NoCommand    chapter6.command.demo2.NoCommand
[slot 5] chapter6.command.demo2.NoCommand    chapter6.command.demo2.NoCommand
[slot 6] chapter6.command.demo2.NoCommand    chapter6.command.demo2.NoCommand

灯->开灯
立体声音响->打开
门->开门
灯->关灯
灯->开灯
立体声音响->关闭
门->关门
门->开门

分析:
(1)NoCommand类虽然什么都不做,但却是有用的,在控制类SimpleRemoteControl的构造函数中,为打开类和关闭类都初始化为NoCommand,这是为了使得后续的操作不必再判断是否为null。
(2)可以使得程序对实体类(Light、Stereo、GarageDoor)的耦合度降低,所有的命令都实现Command接口,利用多态的特性,在控制类SimpleRemoteControl写出高可用性,在setCommand中可以设置任意设备类型的开和关。
(3)注意到,构造函数中只定义了7个开和关,优化后可以使用两个List,分别记录开和关命令,之后的setCommand就改成addCommand(Command on, Command off)。最后的onButtonPressed和offButtonPressed可以判断下标是否越界。
(4)实际上,每个具体的Command都可以把引用变量修改为引用变量数组,即控制多台相同的设备。execute和undo方法中都用循环来操作。
类图如下:
命令模式

三、总结

命令模式的核心是把请求封装成对象,如把一个请求封装到线程中,通常用来处理请求队列、日志操作、事务操作,并且还支持撤销操作(相当于ctrl+z)。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

学无止境jl

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

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

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

打赏作者

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

抵扣说明:

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

余额充值