在软件系统中,“
行为请求者”与“
行为实现者”通常呈现一种“
紧耦合”。但在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的。在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将
一组行为抽象为对象,
实现二者之间的松耦合。这就是
命令模式(Command Pattern)————题记
设计模式
命令模式:将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。
模式分析
1. 命令模式的本质是
对命令进行封装,将发出命令的责任和执行命令的责任分割开。
2. 每一个命令都是一个操作:
请求的一方发出请求,要求执行一个操作;接收的一方收到请求,并执行操作。
3. 命令模式允许请求的一方和接收的一方独立开来,
使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。
4. 命令模式使
请求本身成为一个对象,这个对象和其他对象一样可以被存储和传递。
5. 命令模式的关键在于
引入了抽象命令接口,且
发送者针对抽象命令接口编程,只有实现了抽象命令接口的
具体命令才能与接收者相关联。
// Invoker:要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。
public class RemoteControl {
//这时候,遥控器要处理7个开与关的命令,使用相应数组记录这些命令
Command[] onCommands;
Command[] offCommands;
public RemoteControl() {
//在构造器中,只需要实例化并初始化两个开与关的数组
onCommands = new Command[7];
offCommands = new Command[7];
//NoCommand对象是一个空对象(null obeject)的例子。当你不想返回一个有意义的对象时,空对象就很有用,客户也可以将处理null的责任转义给空对象。
//举例来说,遥控器不可能一出厂就设置了有意义的命令对象,所以提供了NoCommand对象作为替代品,当调用它的execute()
//方法时,这种对象什么也不做。
Command noCommand = new NoCommand();
for (int i = 0; i < 7; i++) {
onCommands[i] = noCommand;
offCommands[i] = noCommand;
}
}
//setCommand()方法须有三个参数,分别是插槽的位置、开的命令、关的命令。
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();
}
//覆盖toString(),打印出每个插槽和它对应的命令。
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 interface Command {
public void execute();
}
//命令接口实现对象,是“虚”的实现;通常会持有接收者,并调用接收者的功能来完成命令要执行的操作。
public class StereoOnWithCDCommand implements Command {
Stereo stereo;
public StereoOnWithCDCommand(Stereo stereo) {
this.stereo = stereo;
}
public void execute() {
stereo.on();
stereo.setCD();
stereo.setVolume(11);
}
}
//接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。
public class Stereo {
String location;
public Stereo(String location) {
this.location = location;
}
public void on() {
System.out.println(location + " stereo is on");
}
public void off() {
System.out.println(location + " stereo is off");
}
public void setCD() {
System.out.println(location + " stereo is set for CD input");
}
public void setDVD() {
System.out.println(location + " stereo is set for DVD input");
}
public void setRadio() {
System.out.println(location + " stereo is set for Radio");
}
public void setVolume(int volume) {
// code to set the volume
// valid range: 1-11 (after all 11 is better than 10, right?)
System.out.println(location + " Stereo volume set to " + volume);
}
}
//测试
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);
StereoOffCommand stereoOff =
new StereoOffCommand(stereo);
//现在已经有了全部的命令,可以将他们加载到遥控器插槽中。
remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff);
remoteControl.setCommand(1, kitchenLightOn, kitchenLightOff);
remoteControl.setCommand(2, ceilingFanOn, ceilingFanOff);
remoteControl.setCommand(3, stereoOnWithCD, stereoOff);
//在这里,使用toString()方法打印出每个遥控器的插槽和它被指定的命令
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);
}
}