命令模式是什么?
命令模式将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象,命令模式也支持可撤销的操作。
命令模式解决什么问题?
现有智能家居Tv和Light,万能遥控器可以控制家居,遥控器每排有开关两个按钮,最后还有一个撤销按钮,假设第一排的开关用于Tv,第二排的开关用于Light:
class Tv {
public void TvOn() {
System.out.println("开电视");
}
public void TvOff() {
System.out.println("关电视");
}
}
class Light {
public void LightOn() {
System.out.println("开灯");
}
public void LightOff() {
System.out.println("关灯");
}
}
public class RemoteControl {
private Tv tv;
private Light light;
public void setTv(Tv tv) {
this.tv = tv;
}
public void setLight(Light light) {
this.light = light;
}
public void row1On() {
if (tv != null) {
tv.TvOn();
}
}
public void row1Off() {
if (tv != null) {
tv.TvOff();
}
}
public void row2On() {
if (light != null) {
light.LightOn();
}
}
public void row2Off() {
if (light != null) {
light.LightOff();
}
}
}
调用过程:
Tv tv=new Tv();
RemoteControl rc=new RemoteControl();
rc.setTv(tv);
rc.row1On();
rc.row1Off();
- problem1:按钮对应的开关事件不能动态改变
- problem2:每当增加新家居时,都要修改源代码设置开关事件
- problem3:撤销按钮事件难以维护
命令模式实现
命令接口
interface Command {
void execute();
void undo();
}
命令实现
保持Light和Tv源代码不变,封装OnCommand和OffCommand,重写execute()调用开或关,重写undo()调用相反的操作:
class TvOnCommand implements Command {
Tv tv;
public TvOnCommand(Tv tv) {
this.tv = tv;
}
@Override
public void execute() {
tv.TvOn();
}
@Override
public void undo() {
tv.TvOff();
}
}
class TvOffCommand implements Command {
Tv tv;
public TvOffCommand(Tv tv) {
this.tv = tv;
}
@Override
public void execute() {
tv.TvOff();
}
@Override
public void undo() {
tv.TvOn();
}
}
class LightOnCommand implements Command {
Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.LightOn();
}
@Override
public void undo() {
light.LightOff();
}
}
class LightOffCommand implements Command {
Light light;
public LightOffCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.LightOff();
}
@Override
public void undo() {
light.LightOn();
}
}
空命令
加入空命令可避免接下来在RemoteControl 中判断命令是否存在:
class NoCommand implements Command{
@Override
public void execute() {
}
@Override
public void undo() {
}
}
命令调用者
修改RemoteControl,通过构造函数传入遥控器行数,方便以后扩展,将全部先设为noCommand避免判空,setCommand()设置行数对应的开关事件,按完按钮后记录撤销事件:
public class RemoteControl {
Command[] onCommands;
Command[] offCommands;
Command undoCommand;
public RemoteControl(int row) {
onCommands = new Command[row];
offCommands = new Command[row];
Command noCommand = new NoCommand();
for (int i = 0; i < row; i++) {
onCommands[i] = noCommand;
offCommands[i] = noCommand;
}
undoCommand = noCommand;
}
public void setCommand(int row, Command onCommand, Command offCommand) {
onCommands[row] = onCommand;
offCommands[row] = offCommand;
}
public void buttonOn(int row) {
onCommands[row].execute();
undoCommand = onCommands[row];
}
public void buttonOff(int row) {
offCommands[row].execute();
undoCommand = offCommands[row];
}
public void undo(){
undoCommand.execute();
}
}
命令过程
Light light=new Light();
LightOnCommand lightOn=new LightOnCommand(light);
LightOffCommand lightOff=new LightOffCommand(light);
RemoteControl rc=new RemoteControl();
rc.setCommand(0,lightOn,lightOff);
rc.buttonOn(0);
rc.buttonOff(0);
rc.undo();
组合命令
利用组合命令可以实现一连串的命令过程,如智能家居的电影模式、温馨模式
class CombinationCommand implements Command {
Command[] commands;
public CombinationCommand(Command[] commands) {
this.commands = commands;
}
@Override
public void execute() {
for (int i = 0; i < commands.length; i++) {
commands[i].execute();
}
}
@Override
public void undo() {
for (int i = 0; i < commands.length; i++) {
commands[i].undo();
}
}
}
组合和命令过程
Light light=new Light();
LightOnCommand lightOn=new LightOnCommand(light);
LightOffCommand lightOff=new LightOffCommand(light);
Tv tv=new Tv();
TvOnCommand tvOn=new TvOnCommand(tv);
TvOffCommand tvOff=new TvOffCommand(tv);
Command[] combinationOn={lightOn,tvOn};
Command[] combinationOff={lightOff,tvOff};
RemoteControl rc=new RemoteControl();
rc.setCommand(0,combinationOn,combinationOff);
rc.buttonOn(0);
rc.buttonOff(0);
rc.undo();