命令模式(Command Pattern)
来源:互联网 发布:公司outlook邮箱域名 编辑:程序博客网 时间:2024/06/05 10:55
1命令模式是一个高内聚的模式。
定义如下:将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。
2.角色说明:
● Receive接收者角色
该角色就是干活的角色,命令传递到这里是应该被执行的。
● Command命令角色
需要执行的所有命令都在这里声明。
● Invoker调用者角色
接收到命令,并执行命令。在例子中,我(项目经理)就是这个角色。
通用类图如下:
3.对命令模式的理解
1.命令模式的本质是对命令进行封装,将发出命令的责任和执行命令的责任分割开。
2.每一个命令都是一个操作:请求的一方发出请求,要求执行一个操作;接收的一方收到请求,并执行操作。
3.命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否被执行、何时被执行,以及是怎么被执行的。
4.命令模式使请求本身成为一个对象,这个对象和其他对象一样可以被存储和传递。
5.命令模式的关键在于引入了抽象命令接口,且发送者针对抽象命令接口编程,只有实现了抽象命令接口的具体命令才能与接收者相关联。
4.适用场景
1.系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互。
2.系统需要在不同的时间指定请求、将请求排队和执行请求。
3.系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。
4.系统需要将一组操作组合在一起,即支持宏命令。
5.模式优点
1.降低对象之间的耦合度。
2.新的命令可以很容易地加入到系统中。
3.可以比较容易地设计一个组合命令。
4.调用同一方法实现不同的功能
6.模式缺点
使用命令模式可能会导致某些系统有过多的具体命令类。因为针对每一个命令都需要设计一个具体命令类,因此某些系统可能需要大量具体命令类,这将影响命令模式的使用。
7.代码示例
/** * 定义了一个电灯作为命令的接受(执行)者 * Created by yangjiachang on 2016/9/11. */public class Light { public void on(){ System.out.println(" 开灯"); } public void off(){ System.out.println(" 关灯"); }}
/** * 定义了一个电视机作为命令的接受(执行)者 * Created by yangjiachang on 2016/9/11. */public class TV { public void on(){ System.out.println(" 打开电视"); } public void off(){ System.out.println(" 关闭电视"); }}
/**
* 命令接口 * Created by yangjiachang on 2016/9/11. */public interface Command { /** * 执行命令 */ void execute(); /** * 撤销命令 */ void undo();}
/** * 开灯命令 * Created by yangjiachang on 2016/9/11. */public class LightOnCommand implements Command { private Light light; public LightOnCommand(Light light){ this.light=light; } @Override public void execute() { light.on(); } @Override public void undo() { light.off(); }}
/** * 关灯命令 * Created by yangjiachang on 2016/9/11. */public class LightOffCommand implements Command { private Light light; public LightOffCommand(Light light){ this.light=light; } @Override public void execute() { light.off(); } @Override public void undo() { light.on(); }}
/** * 开电视命令 * Created by yangjiachang on 2016/9/11. */public class TVOnCommand implements Command { private TV tv; public TVOnCommand(TV tv){ this.tv=tv; } @Override public void execute() { tv.on(); } @Override public void undo() { tv.off(); }}
/** * 关电视命令 * Created by yangjiachang on 2016/9/11. */public class TVOffCommand implements Command { private TV tv; public TVOffCommand(TV tv){ this.tv=tv; } @Override public void execute() { tv.off(); } @Override public void undo() { tv.on(); }}
/** * 调用者:遥控器 * 可对对电灯、电视进行开关、撤销、重做命令 * * Created by yangjiachang on 2016/9/11. */public class RemoteController { /** * 记录重做命令 */ private Stack<Command> redoCommands = new Stack<Command>(); /** * 记录撤销命令 */ private Stack<Command> undoCommands = new Stack<Command>();; public void execute(Command command){ System.out.println("执行命令"); command.execute(); undoCommands.add(command); } public void undo(){ System.out.println("撤销命令"); if (undoCommands.empty()){ return; } Command lastCommand = undoCommands.pop(); lastCommand.undo(); redoCommands.push(lastCommand); } public void redo(){ System.out.println("重新执行命令"); if (redoCommands.empty()){ return; } Command lastCommand = redoCommands.pop(); lastCommand.execute(); undoCommands.push(lastCommand); }
执行结果:public static void main(String[] args) { RemoteController remoteController = new RemoteController(); remoteController.execute(new LightOnCommand(new Light())); remoteController.execute(new TVOnCommand(new TV())); remoteController.undo(); remoteController.undo(); remoteController.redo(); remoteController.redo();}
执行命令
开灯
执行命令
打开电视
撤销命令
关闭电视
撤销命令
关灯
重新执行命令
开灯
重新执行命令
打开电视
在这里例子中,invoker调用者是一个多功能遥控器,可以开关灯和电视机,当然还可以扩展很多的receiver。以电灯为例,分别将开灯和关掉的命令封装成了LightOnCommand和LightOffCommand对象,每个具体命令对象都实现了Command命令接口。在这个例子中,client使用invoker遥控器来完成具体命令execute(),还能撤销命令undo()(实际上就是反向的命令)和重新执行命令redo()。因此我们需要记录这些命令已经先后顺序,这里使用了Stack来记录。遥控器中的两个Stack,undoCommands用来来保存所有的可以取消的操作,redoCommands用来保存所有重做的操作。方法执行的时候,首先调用某个Command的Execute方法,然后将该命令对象Push到undoCommands的Stack上,待以后撤销使用:
有了以上数据结构,撤销和重做逻辑就很简单,当用户点击撤销的时候:
1.首先检查 undoCommands是否为空,如果为空,直接返回,否则继续.
2.从undoCommands中Pop出最近一次的操作对象Command对象
3.然后将该命令对象Push到redoCommands上保存以便以后重做。
4.最后Pop出来的命令对象Command的undo方法实现撤销.
和撤销类似,当用户点击重做的时候
1.首先检查redoCommands是否为空,如果为空,直接返回,否则继续.
2.从redoCommands中Pop出最近一次的操作对象Command对象
3.然后将该命令对象Push到undoCommands上保存以便以后撤销。
4.最后Pop出来的命令对象Command的execute方法实现重做.
刚刚都是以单个命令来执行的,下面举个宏命令的例子:
将上面的遥控器换成一个宏命令处理器
/** * 宏命令处理 * * Created by yangjiachang on 2016/9/11. */public class MacroCommandController { /** * 宏命令 */ private Command[] commands; /** * 重新执行宏命令 */ private Stack<Command[]> redoMacroCommand = new Stack<Command[]>(); /** * 撤销宏命令 */ private Stack<Command[]> undoMacroCommand = new Stack<Command[]>(); public void execute(Command... commands){ System.out.println("执行宏命令"); this.commands = commands; for (Command command : commands){ command.execute(); } undoMacroCommand.add(commands); } public void undo(){ System.out.println("撤销宏命令"); if (undoMacroCommand.empty()){ return; } Command[] lastCommand = undoMacroCommand.pop(); for (Command command : lastCommand){ command.undo(); } redoMacroCommand.push(lastCommand); } public void redo(){ System.out.println("重新执行宏命令"); if (redoMacroCommand.empty()){ return; } Command[] lastCommand = redoMacroCommand.pop(); for (Command command : lastCommand){ command.execute(); } undoMacroCommand.push(lastCommand); }}测试方法
public static void main(String[] args) { MacroCommandController macro = new MacroCommandController(); macro.execute(new LightOnCommand(new Light()),new TVOnCommand(new TV())); macro.undo(); macro.redo();}测试结果
执行宏命令
开灯
打开电视
撤销宏命令
关灯
关闭电视
重新执行宏命令
开灯
打开电视
8.其他例子
1.ibatis在处理不同的CRUD语句时,通过SqlCommand的类型来区别执行不同的增删改查方法。
2.消息队列:你在某一段添加命令,然后另一端的线程从队列中取出命令,调用他的execute方法,等待这个调用完成,然后将此命令对象丢弃,再取下一个命令……
3.日志请求:某些应用需要将所有的动作记录在日志(磁盘)中,并能在系统死机之后,重新加载并成批地依次调用这些命令对象,从而恢复到之前的状态。
- 命令模式(Command Pattern)
- 命令模式(Command Pattern)
- 命令模式(Command Pattern)
- 命令模式(Command Pattern)
- 命令模式(Command Pattern)
- 命令模式(Command Pattern)
- 命令模式(Command Pattern)
- 命令模式(Command Pattern)
- 命令模式(Command Pattern)
- 命令模式(command pattern)
- 命令模式(Command Pattern)。
- 命令模式(Command Pattern)
- 命令模式(Command Pattern)
- Command Pattern(命令模式)
- 命令模式 Command Pattern
- 命令模式【Command Pattern】
- 命令模式(Command Pattern)
- 命令模式(Command Pattern)
- bzoj 1568: [JSOI2008]Blue Mary开公司(超哥线段树)
- ASP.NET SignalR- High-Frequency Realtime with SignalR
- Poedu_C语言_lesson08_20160906_第一个程序
- 【计算机视觉】<前景检测>ViBe - a powerful technique for background detection and subtraction in video sequences
- atmega328p use printf
- 命令模式(Command Pattern)
- ASP.NET SignalR-Server Broadcast with SignalR
- Bootstrap其他内置组件(二)
- 2015-2016 ACM-ICPC, NEERC, Northern Subregional Contest Problem J 【二分+DP+单调队列】
- 双栈问题
- poj 2706 Connect
- 怎么在sql server建表时设置时间字段自动生成值
- const的用法及其重要性
- iOS 多面传值之 -- 单例传值