命令
将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化,对请求排队或记录请求日志,以及支持可撤销的操作。
命令模式(Command)是指,把请求封装成一个命令,然后执行该命令。
在使用命令模式前,我们先以一个编辑器为例子,看看如何实现简单的编辑操作:
public class TextEditor {
private StringBuilder buffer = new StringBuilder();
public void copy() {
...
}
public void paste() {
String text = getFromClipBoard();
add(text);
}
public void add(String s) {
buffer.append(s);
}
public void delete() {
if (buffer.length() > 0) {
buffer.deleteCharAt(buffer.length() - 1);
}
}
public String getState() {
return buffer.toString();
}
}
我们用一个StringBuilder
模拟一个文本编辑器,它支持copy()
、paste()
、add()
、delete()
等方法。
正常情况,我们像这样调用TextEditor
:
TextEditor editor = new TextEditor();
editor.add("Command pattern in text editor.\n");
editor.copy();
editor.paste();
System.out.println(editor.getState());
这是直接调用方法,调用方需要了解TextEditor
的所有接口信息。
如果改用命令模式,我们就要把调用方发送命令和执行方执行命令分开。怎么分?
解决方案是引入一个Command
接口:
public interface Command {
void execute();
}
调用方创建一个对应的Command
,然后执行,并不关心内部是如何具体执行的。
为了支持CopyCommand
和PasteCommand
这两个命令,我们从Command
接口派生:
public class CopyCommand implements Command {
// 持有执行者对象:
private TextEditor receiver;
public CopyCommand(TextEditor receiver) {
this.receiver = receiver;
}
public void execute() {
receiver.copy();
}
}
public class PasteCommand implements Command {
private TextEditor receiver;
public PasteCommand(TextEditor receiver) {
this.receiver = receiver;
}
public void execute() {
receiver.paste();
}
}
最后我们把Command
和TextEditor
组装一下,客户端这么写:
TextEditor editor = new TextEditor();
editor.add("Command pattern in text editor.\n");
// 执行一个CopyCommand:
Command copy = new CopyCommand(editor);
copy.execute();
editor.add("----\n");
// 执行一个PasteCommand:
Command paste = new PasteCommand(editor);
paste.execute();
System.out.println(editor.getState());
这就是命令模式的结构:
┌──────┐ ┌───────┐
│Client│─ ─ ─>│Command│
└──────┘ └───────┘
│ ┌──────────────┐
├─>│ CopyCommand │
│ ├──────────────┤
│ │editor.copy() │─ ┐
│ └──────────────┘
│ │ ┌────────────┐
│ ┌──────────────┐ ─>│ TextEditor │
└─>│ PasteCommand │ │ └────────────┘
├──────────────┤
│editor.paste()│─ ┘
└──────────────┘
有的童鞋会有疑问:搞了一大堆Command
,多了好几个类,还不如直接这么写简单:
TextEditor editor = new TextEditor();
editor.add("Command pattern in text editor.\n");
editor.copy();
editor.paste();
实际上,使用命令模式,确实增加了系统的复杂度。如果需求很简单,那么直接调用显然更直观而且更简单。
那么我们还需要命令模式吗?
答案是视需求而定。如果TextEditor
复杂到一定程度,并且需要支持Undo、Redo的功能时,就需要使用命令模式,因为我们可以给每个命令增加undo()
:
public interface Command {
void execute();
void undo();
}
然后把执行的一系列命令用List
保存起来,就既能支持Undo,又能支持Redo。这个时候,我们又需要一个Invoker
对象,负责执行命令并保存历史命令:
┌─────────────┐
│ Client │
└─────────────┘
│
│
▼
┌─────────────┐
│ Invoker │
├─────────────┤ ┌───────┐
│List commands│─ ─>│Command│
│invoke(c) │ └───────┘
│undo() │ │ ┌──────────────┐
└─────────────┘ ├─>│ CopyCommand │
│ ├──────────────┤
│ │editor.copy() │─ ┐
│ └──────────────┘
│ │ ┌────────────┐
│ ┌──────────────┐ ─>│ TextEditor │
└─>│ PasteCommand │ │ └────────────┘
├──────────────┤
│editor.paste()│─ ┘
└──────────────┘
可见,模式带来的设计复杂度的增加是随着需求而增加的,它减少的是系统各组件的耦合度。
练习
给命令模式新增Add和Delete命令并支持Undo、Redo操作。
小结
命令模式的设计思想是把命令的创建和执行分离,使得调用者无需关心具体的执行过程。
通过封装Command
对象,命令模式可以保存已执行的命令,从而支持撤销、重做等操作。
相关文章
- ps工具栏快捷键大全-详细介绍5种PS中常用的抠图技巧,助你快速抠出你想要的区域
- 电力煤矿液体泄漏识别系统
- 视频监控边缘分析盒
- 河道水尺水位监测系统
- ps工具栏快捷键大全-大神教你这10招PS操作技巧, 提高你的工作效率
- 占道经营出店摆摊监测识别系统
- 热成像成像不清楚是什么时候_红外热成像技术竟然可以做这些事情?
- 西门子SCL---气缸逻辑控制
- 红外测试操作步骤_红外热成像技术竟然可以做这些事情?(二)
- 口罩佩戴监测系统
- 模态对话框-B 类产品设计细节:对话框 vs 抽屉
- 人脸打卡机怎么作弊-指纹考勤机的常见问题及解决方法
- 循环队列出队-循环队列的c语言实现
- 循环队列出队-数据结构与算法 | 循环队列
- 河道非法采砂识别系统
- 循环队列出队-栈和队列的实现
- 循环队列出队-单个指针下循环链表的入队与出队
- spring 有哪些日志级别-LogBack 日志等级设置无效,原因竟然是因为这个
- 循环队列出队-队列,顺序队列与循环队列
- 渣土车空车未盖盖识别系统