JAVA设计模式笔记整理(四)

6.命令模式

定义:

将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。

类图:

示例:巴斯特家电自动化公司想让你设计一个家电自动化遥控器。这个遥控器有七个可编程的插槽(每个都可以指定到一个不同的家电装置),每个插槽都有对应的开关按钮。整个遥控器还有一个整体的撤销按钮。巴斯特家电自动化公司希望你能够创建一组控制遥控器的API,让每个插槽都能够控制一个或一组装置,能够控制目前的装置和任何未来可能出现的装置。

思考:应该遥控器应该知道如何解读按钮被按下的动作,然后发出正确的请求。但是遥控器不需知道这些家电自动化的细节。“动作的请求者”须从“动作的执行者”对象中解耦,这正是命令模式能做的。在这里请求者为遥控器,执行者是具体的家电厂商类的实例。具体而言:利用命令对象,把请求(例如开灯)封装成一个特定对象。对每个按钮都存储一个命令对象,那么当按钮被按下的时候,只要有个命令对象能和正确的对象沟通,把事情做好就可以了。这样,遥控器和电灯对象便解耦了。

Client对应于遥控器设计者

Command对应于具体的命令(开关)

Invoker对应于遥控器

Receiver对应于具体的家电设备

现在先实现命令接口

public interface Command {public void  execute();}

所有的命令对象都应实现这个接口

现在再实现一个打开电灯的命令

public class LightOnCommand implements Command {Light light;public LightOnCommand(Light light){this.light=light;}public void execute(){light.on();}}

现在假设遥控器只有一个插槽,可以这样控制命令对象

public class SimpleRemoteControl {Command slot;public SimpleRemoteControl(){}public void setCommand(Command command){slot=command;}public void buttonWasPressed(){slot.execute();}}

简单测试:

public class Light {public void on(){System.out.println("Light is on");}}
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();}}

结果:

Light is on

现在在Command接口加入撤销Undo()方法:

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

接着修改LightOnCommand

public class LightOnCommand implements Command {Light light;public LightOnCommand(Light light){this.light=light;}public void execute(){light.on();}public void undo(){light.off();}public String toString(){return "LightOnCommand";}}

增加LightOffCommand

public class LightOffCommand implements Command {Light light;public LightOffCommand(Light light){this.light=light;}public void execute(){light.off();}public void undo(){light.on();}public String toString(){return "LightOffCommand";}}

修改剩余代码,这里假定遥控器的七个插槽只用了一个插槽

public class Light {public void on(){System.out.println("Light is on");}public void off(){System.out.println("Light is off");}}
public class Light {private String name;public Light(String name){this.name=name;}public void on(){System.out.println(name+" Light is on");}public void off(){System.out.println(name+" Light is off");}}
public class NoCommand implements Command {public void execute(){}public void undo(){}public String toString(){return "NoCommand";}}
public class SimpleRemoteControl {Command[] onCommands;Command[] offCommands;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 onButtonWasPressed(int slot){onCommands[slot].execute();undoCommand=onCommands[slot];}public void offButtonWasPressed(int slot){offCommands[slot].execute();undoCommand=offCommands[slot];}public void undoButtonWasPressed(){undoCommand.undo();}public String toString(){String s="";for(int i=0;i<7;i++){s+="[slot "+i+"]"+onCommands[i]+" "+offCommands[i]+"\n";}s+="[undo "+"]"+undoCommand;return s;}}
public class RemoteControlTest {public static void main(String[] args){SimpleRemoteControl remoteControl=new SimpleRemoteControl();Light livingRoomLight=new Light("Living Room");LightOnCommand livingRoomLightOn=new LightOnCommand(livingRoomLight);LightOffCommand livingRoomLightOff=new LightOffCommand(livingRoomLight);remoteControl.setCommand(0, livingRoomLightOn, livingRoomLightOff);remoteControl.onButtonWasPressed(0);remoteControl.offButtonWasPressed(0);System.out.println(remoteControl);remoteControl.undoButtonWasPressed();remoteControl.offButtonWasPressed(0);remoteControl.onButtonWasPressed(0);System.out.println(remoteControl);remoteControl.undoButtonWasPressed();}}

结果:

Living Room Light is onLiving Room Light is off[slot 0]LightOnCommand  LightOffCommand[slot 1]NoCommand  NoCommand[slot 2]NoCommand  NoCommand[slot 3]NoCommand  NoCommand[slot 4]NoCommand  NoCommand[slot 5]NoCommand  NoCommand[slot 6]NoCommand  NoCommand[undo ]LightOffCommandLiving Room Light is onLiving Room Light is offLiving Room Light is on[slot 0]LightOnCommand  LightOffCommand[slot 1]NoCommand  NoCommand[slot 2]NoCommand  NoCommand[slot 3]NoCommand  NoCommand[slot 4]NoCommand  NoCommand[slot 5]NoCommand  NoCommand[slot 6]NoCommand  NoCommand[undo ]LightOnCommandLiving Room Light is off

宏命令:

如果要一个按键启动多个命令,可以采用宏命令,用如下的形式

public class MacroCommand implements Command {Command[] commands;public MacroCommand(Command[] commands){this.commands=commands;}public void execute(){for(int i=0;i<commands.length;i++){commands[i].execute();}}public void undo(){for(int i=0;i<commands.length;i++){commands[i].undo();}}}

宏命令对调用者来说,可以和普通命令一样使用

要点:

命令模式将发出请求的对象和执行请求的对象解耦

在被解耦的两者之间是通过命令对像进行沟通的。命令对象封装了接收者和一个或一组动作

调用者通过调用命令对象的execute()发出请求,这会使得接收者的动作被调用

调用者可以接受命令当做参数,甚至在运行时动态地进行

命令可以支持撤销,做法是实现一个undo()方法来回到execute()被执行前的状态

宏命令是命令的一种简单的延伸,允许调用多个命令。宏方法也可以支持撤销

实际操作时,很常见使用"聪明"命令对象,也就是直接实现了请求,而不是将工作委托给接收者

命令也可以用来实现日志和事务系统

最快乐的时候,就是去旅行。

JAVA设计模式笔记整理(四)

相关文章:

你感兴趣的文章:

标签云: