设计模式-命令模式(Command Pattern)
本文由@呆代待殆原创,转载请注明出处:http://www.cnblogs.com/coffeeSS/
命令模式简述
命令模式的主要作用是将“行为请求者”和“行为实现者”解耦。举个例子,假如我们现在要使用machine类的work()方法,一般我们会在客户端直接生成machine类的实例然后调用machine.work(),这时客户端是行为请求者,而machine类时行为实现者,他们两个就呈现一种紧耦合的状态,这种紧耦合状态是我们不期望的,而此时我们可以借助命令模式来进行解耦。
命令模式的定义与基本结构
定义:将请求封装成一个对象,以便参数化其他不同种类的对象(行为实现者)。同时命令模式也能支持撤销操作。
一张来自《Head First》的结构图
Receiver:行为实现者,真正包含方法代码实现的类,action()方法就是实际会被调用的方法,这个类会作为ConcreteCommand类的参数被封装进ConcreteCommand类。
Command:是一个接口,每一个ConcreteCommand类都会继承这个接口,其中的execute()方法会提供给Invoker类调用,execute()方法里会调Command里封装的Receiver类的方法,undo()用来撤销execute()的调用,也是提供给invoker使用的。
ConcreteCommand:继承了Command并封装一个Receiver类,是方法调用者和方法实现者之间沟通的桥梁,方法调用者Invoker只需调用execute()方法即可,无需知道内部实现,而ConcreteCommand负责把需要Receiver方法封装进execute()中。
Invoter:行为调用者,只负责装入需要的Command类型对象然后调用execute()方法即可,无需关系行为的实现。
Client:这里的客户端和普通意义不太一样,翻译成装配者更好,它负责生产ConcreteCommand对象,并将合适的Receiver装入其中。
小结:从上面的介绍可以看出Invoter作为行为调用者,唯一的任务就是装入合适的Command类然后调用execute()方法。而具体的行为细节则由封装在ConcreteCommand里的Receiver实现。这就实现了Invoter与Receiver的解耦,当我们加入新的Receiver时,我们只需要写一个新的ConcreteCommand类就行,之前的代码不需要做任何的更改。
一个简单的实例(java)
我们借用Head first上的例子。这个例子中我们需要编码一个远程控制遥控器,来控制一些电器的功能,如电视的开关,灯的明灭以及未来可能新出现的一些电器的控制。每一个电器都有自己的类和方法来控制自己的功能,这里给出电视类和灯类作为例子。(这两个类就是Receiver类,他们包含了行为的具体实现)
public class TvReceiver { public boolean isOn=false; public void on(){ isOn=true; System.out.println("open the TV"); } public void off(){ isOn=false; System.out.println("close the TV"); } } public class LampReceiver { public boolean isOn=false; public void on(){ isOn=true; System.out.println("open the lamp"); } public void off(){ isOn=false; System.out.println("close the lamp"); } }
然后我们给出Command接口和,和对应电视和灯的lamp类,这里我们假设我们的远程遥控器有一堆可编程的按钮每一个按钮都一个行为调用者的物理具现。所以我们一共需要写四个ConcreteCommand类,对应灯和电视的4个操作,这里我们只写出Tv的两个作为例子。
public interface Command { public abstract void execute(); public abstract void undo(); } public class CommandTvOn implements Command { private TvReceiver tv; private boolean lastStatus=false; CommandTvOn(TvReceiver tv){ this.tv=tv; lastStatus=tv.isOn; } @Override public void execute() { lastStatus=tv.isOn; tv.on(); } @Override public void undo() { if(lastStatus==false) tv.off(); else System.out.println("无需执行undo"); } } public class CommandTvOff implements Command { private TvReceiver tv; private boolean lastStatus=false; CommandTvOff(TvReceiver tv){ this.tv=tv; lastStatus=tv.isOn; } @Override public void execute() { lastStatus=tv.isOn; tv.off(); } @Override public void undo() { if(lastStatus==true) tv.on(); else System.out.println("无需执行undo"); } }
这里的undo实现只实现了撤销一步,因为只记录了一个历史状态,如果要完成多步撤销就要记录一个状态数组,这里就不写了。
最后我们写出我们的遥控器类,就是Invoter类的实现,我们默认这个遥控器只有四个按钮,其实我们只用两个就够了,其中出现的NoCommand类,也是继承了Command接口的,只不过继承的方法都为空,这里用来初始化遥控器,相当于NULL值一样的存在。
public class ControlMachine { Command[] CommandButton; Command undoCommand=new NoCommand(); ControlMachine(){ CommandButton=new Command[4]; for(Command c:CommandButton) c=new NoCommand(); } public void setCommand(int number,Command c){ CommandButton[number]=c; } public void pushButton(int number){ CommandButton[number].execute(); undoCommand=CommandButton[number]; } public void pushUndo(){ undoCommand.undo(); } }
最后我们给出测试类,装载Command的工作也一并在测试中进行。
public class Text { public static void main(String []args){ ControlMachine cm=new ControlMachine(); TvReceiver tv=new TvReceiver(); cm.setCommand(0, new CommandTvOn(tv)); cm.setCommand(1, new CommandTvOff(tv)); cm.pushButton(0); cm.pushUndo(); cm.pushButton(0); cm.pushButton(1); cm.pushUndo(); cm.pushButton(1); cm.pushButton(1); cm.pushUndo(); } }
输出如下:
命令模式到此结束♪(^∇^*)。
参考资料:《Head First 设计模式》。