在讲命令模式前,我们先用一个小案例来说明。
小明又接受到了一个只能家居的项目,其实要实现的功能也不多,就是让遥控器指定的按钮能够控制指定的家电,比如点灯,音响等。小明一下子就想到了这不就是一系列命令吗,然后就着手用命令模式去做。下面是小明对整个项目的分析类图:
然后小明给出了分析思路:
把设备里面的具体命令功能进行封装,抽象成命令对象或者命令类,它的体系结构:
第一步:抽象出一个命令的接口Command,命令接口里面具体有execute和undo方法,具体实现类实现这两个方法控制器control设计里面
第二步:对指定的按钮不是具体设备的功能,而是只执行命令对象里面的execute函数,所以在控制器设计的时候,里面的按钮关联的是命令接口和命令对象,在setCommand方法中把具体的按钮和具体的命令对象联合起来,这样做的话就把遥控器按钮的执行跟具体设备的执行相解耦合
这样做的好处:这样可以使两边独立的扩展发展独立进化,对扩展的开放,对修改的关闭很好的结合在一起。
先给出两个实体类灯(Light)和音响类(Stereo)
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 {
//音量初始化为0
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("Stream volume="+vol);
}
public int GetVol(){
return volume;
}
public void Start(){
System.out.println("Stereo Start");
}
}
然后给出命令接口Command:
public interface Command {
public void execute();
public void undo();
}
具体的命令实现分别为:LightOnCommand、LightOffCommand、StereoOnCommand、StereoOffCommand、StereoAddVolCommand、StereoSubVolCommand和一个无实现的NoCommand,对于NoCommand在后面解释。
对于以上的命令实现就不一一贴出代码,贴出StereoOnCommand的示例一下:
public class StereoOnCommand implements Command {
private Stereo stereo;
public StereoOnCommand(Stereo stereo){
this.stereo=stereo;
}
public void execute() {
stereo.On();
stereo.SetCd();
}
public void undo() {
stereo.Off();
}
}
接着,我们给出控制器的接口Control:
public interface Control {
//打开按钮指定的电器
public void onButton(int slot);
//关闭按钮指定的电器
public void offButton(int slot);
//回退按钮
public void undoButton(int slot);
}
在真实的遥控器中我们去实现CommandModelControl:
public class CommandModelControl implements Control {
private Command[] onCommands;
private Command[] offCommands;
private Stack<Command> stack=new Stack<Command>();
public CommandModelControl(){
onCommands=new Command[5];
offCommands=new Command[5];
Command noCommand=new NoCommand();
for(int i=0;i<onCommands.length;i++){
onCommands[i]=noCommand;
offCommands[i]=noCommand;
}
}
public void setCommand(int slot,Command onCommand,Command offCommand){
onCommands[slot]=onCommand;
offCommands[slot]=offCommand;
}
public void onButton(int slot) {
onCommands[slot].execute();
stack.push(onCommands[slot]);
}
public void offButton(int slot) {
onCommands[slot].execute();
stack.push(onCommands[slot]);
}
public void undoButton(int slot) {
stack.pop().undo();;
}
}
这里我们解释一下那个noCommand对象,这是一个小技巧,因为遥控器的每个按钮都对应一个真实的对象,如果不用这个的话,在下面执行的时候,我们每次都要先判断这个遥控器按钮的对象是否存在,然而加入这个noCommand就可以省略这一步了
最后,执行ControlTest :
public class ControlTest {
public static void main(String[] args) {
CommandModelControl control=new CommandModelControl();
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 kitchOn=new LightOnCommand(kitchlight);
LightOffCommand kitchOff=new LightOffCommand(kitchlight);
StereoOnCommand stereOn=new StereoOnCommand(stereo);
StereoOffCommand stereOff=new StereoOffCommand(stereo);
StereoAddVolCommand stereaddvol=new StereoAddVolCommand(stereo);
StereoSubVolCommand steresubvol=new StereoSubVolCommand(stereo);
control.setCommand(0, bedroomlightOn, bedroomlightOff);
control.setCommand(1, kitchOn, kitchOff);
control.setCommand(2, stereOn, stereOff);
control.setCommand(3, stereaddvol, steresubvol);
control.onButton(0);
control.undoButton(0);
//control.offButton(0);
control.onButton(1);
control.onButton(2);
control.onButton(3);
control.offButton(2);
control.offButton(1);
control.offButton(0);
}
}
总结:
命令模式:将请求、命令、动作等封装成对象,这样可以让项目使用这些对象来参数化其他对象。使得命令的请求者和执行者耦合。