命令模式(COMMAND),又称动作(Action),事务(Transaction),通过将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,实现对请求排队或记录请求日志,以及支持可撤销的操作。命令模式可以将请求发送者和接收者完全解耦,发送者与接收者之间没有直接引用关系,发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求。
一、使用场景
1、需要抽象出待执行的动作以参数化某对象时。这种参数化机制在过程语言中是通过回调函数表达式实现的,Command模式则是回调机制的一个面向对象的替代品。
2、在不同的时刻指定、排列和执行请求。一个Command对象可以有一个与初始请求无关的生存期,你完全可以将请求的命令对象传递给另一个不同的进程并在那实现该请求。
3、支持取消操作。在Command的execute操作执行前将状态存储到一个历史列表中,通过向后和向前遍历该列表并分别调用undo和redo方法来实现数量不限的“取消”和"重做"。
4、支持修改日志,当系统崩溃时可以用于修改的重做,以便复原崩溃前状态。在Command接口中可以加入装载和存储操作,用来将系统修改持久化到日志文件。系统恢复只需从日志文件中读入修改记录,然后使用Command的execute重新执行它们即可。
5、用构建在原语操作上的高层操作构造一个系统,比如支持事务的信息系统。一个事务封装了对数据的一组变动,Command模式提供了对事务进行建模的方法。Command提供的公共的接口,可以用来以一致的方式调用所有事务。同时使用命令模式也易于添加新事务以扩展系统。
二、UML图
注: 命令模式的关键在于引入了抽象命令类,请求发送者针对抽象命令类编程,只有实现了抽象命令类的具体命令才与请求接收者相关联。
三、Java实现
package study.patterns.command; import java.util.ArrayList; import java.util.List; /** * 命令模式的本质是对请求进行封装,一个请求对应于一个命令,将发出命令的责任和执行命令的责任分割开。 * 每一个命令都是一个操作:请求的一方发出请求要求执行一个操作;接收的一方收到请求,并执行相应的操作。 * 命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口, * 更不必知道请求如何被接收、操作是否被执行、何时被执行,以及是怎么被执行的。 * @author qbg */ public class CommandPattern { public static void main(String[] args) { //请求接收者(ReceiverA)封装到具体的命令对象(ConcreteCommandA)中,与请求发送者(CommandPattern)分离。 Command comA = new ConcreteCommandA(); Command comB = new ConcreteCommandB(); Command macro = new MacroCommand(); macro.addCommand(comA); macro.addCommand(comB); macro.execute(); macro.undo(); macro.redo(); macro.writeLog("config.log"); macro.readLog("config.log"); } } /** * 抽象命令:采用组合模式实现宏命令(类似于批处理命令,可以执行一批命令) */ interface Command{ /** * 添加命令(组合模式) * @param command */ public void addCommand(Command command); /** * 删除命令(组合模式) * @param command */ public void removeCommand(Command command); /** * 撤销(撤销上次操作) */ public void undo(); /** * 重做(重做上次操作) */ public void redo(); /** * 执行命令 */ public void execute(); /** * 从指定文件读取执行命令,以待重新执行 * @param fileName */ public void readLog(String fileName); /** * 将执行过的命令持久化文件中,以待灾难恢复 * @param fileName */ public void writeLog(String fileName); } /** * 默认实现类,不提供任何实现 */ class DefaultCommand implements Command{ @Override public void addCommand(Command command) { throw new IllegalAccessError("方法未实现"); } @Override public void removeCommand(Command command) { throw new IllegalAccessError("方法未实现"); } @Override public void undo() { throw new IllegalAccessError("方法未实现"); } @Override public void redo() { throw new IllegalAccessError("方法未实现"); } @Override public void execute() { throw new IllegalAccessError("方法未实现"); } @Override public void readLog(String fileName) { throw new IllegalAccessError("方法未实现"); } @Override public void writeLog(String fileName) { throw new IllegalAccessError("方法未实现"); } } /** * 命令接收者A,具体业务处理 */ class ReceiverA{ public void action(){ System.out.println("ReceiverA:do something...."); } } /** * 命令接收者B,具体业务处理 */ class ReceiverB{ public void action(){ System.out.println("ReceiverB:do something...."); } } /** * 具体命令实现,覆盖默认命令类的execute() */ class ConcreteCommandA extends DefaultCommand{ private ReceiverA receiver = new ReceiverA(); @Override public void execute() { receiver.action(); } @Override public String toString(){ return "ConcreteCommandA "; } } /** * 具体命令实现,覆盖默认命令类的execute() */ class ConcreteCommandB extends DefaultCommand{ private ReceiverB receiver = new ReceiverB(); @Override public void execute() { receiver.action(); } @Override public String toString(){ return "ConcreteCommandB "; } } /** * 宏命令:使用组合模式和命令模式,批量执行命令 */ class MacroCommand implements Command{ private List<Command> commands = new ArrayList<Command>(); @Override public void addCommand(Command command) { this.commands.add(command); } @Override public void removeCommand(Command command) { this.commands.remove(command); } /** * 除了通过一个逆向操作来实现撤销(Undo)外,还可以通过保存对象的历史状态来实现撤销, * 后者可使用备忘录模式(Memento Pattern)来实现。 */ @Override public void undo() { if(commands.size()>0){ System.out.println("撤销命令:"+commands.get(commands.size()-1)); } } @Override public void redo() { if(commands.size()>0){ System.out.println("重做命令:"+commands.get(commands.size()-1)); } } @Override public void execute() { for(Command com : commands){ com.execute(); } } /** * 序列化过程省略,Command需要实现java.io.Serializable接口. */ @Override public void readLog(String fileName) { System.out.println("从日志文件:"+fileName+"中加载命令列表..."); } /** * 反序列化过程省略,只是用来说明命令模式可以记录命令日志文件. */ @Override public void writeLog(String fileName) { System.out.println("持久化命令列表:"+commands+"到日志文件:"+fileName); } }运行结果:
ReceiverA:do something.... ReceiverB:do something.... 撤销命令:ConcreteCommandB 重做命令:ConcreteCommandB 持久化命令列表:[ConcreteCommandA , ConcreteCommandB ]到日志文件:config.log 从日志文件:config.log中加载命令列表...
四、模式优缺点
优点:
1、降低系统耦合度。Command模式将调用操作的对象与知道如何实现该操作的对象解耦。
2、增加新的Command很容易,无需改变已有的类,扩展性强。
3、可以将多个命令装配成一个复合命令(宏命令)。
4、为请求的撤销(Undo)和重做(Redo)提供了一种设计和实现方案。
缺点:
1、对于复杂的系统,可能会导致大量具体命令类的产生。
原文:http://blog.csdn.net/qbg19881206/article/details/18503469