命令模式
一、什么是命令模式?
命令模式(Command Design Pattern):将请求封装为一个对象,使得发出请求的责任和执行请求的责任分割开。
命令有时也可以称为事件,比如点击鼠标、按下键盘等事件,通常会将它们封装成一个事件对象,然后按顺序放入队列中,再逐个去处理它们。
二、为什么要用命令模式?
- 大部分编程语言是无法将函数作为参数传递给其他函数的
- 利用命令模式,可以把函数封装成命令对象,模拟函数作为参数
- 命令模式,主要用来控制命令的执行,比如异步、延迟、排队等,同时还支持撤销、重做、存储、统计命令等
三、命令模式的结构是怎么样的?
命令模式包含以下几个角色:
- 抽象命令:负责定义命令接口
- 具体的命令:负责实现命令接口
- 命令接收者:执行命令时的目标对象
- 命令发动者:发动执行命令的角色,或者说持有命令,并开始执行命令的地方

示例程序结构:

抽象命令接口(Command):
1 2 3
| public interface Command { void execute(); }
|
具体的命令(ConcreteCommand):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class DrawCommand implements Command {
protected Drawable drawable; private Point position;
public DrawCommand(Drawable drawable, Point position) { this.drawable = drawable; this.position = position; }
@Override public void execute() { drawable.draw(position.x, position.y); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| public class MacroCommand implements Command {
private Stack commands = new Stack();
@Override public void execute() { Iterator it = commands.iterator(); while (it.hasNext()) { ((Command)it.next()).execute(); } }
public void append(Command cmd) { if (cmd != this) { commands.push(cmd); } }
public void undo() { if (!commands.isEmpty()) { commands.pop(); } }
public void clear() { commands.clear(); } }
|
命令接收者(Receiver):
1 2 3
| public interface Drawable { void draw(int x, int y); }
|
命令发动者(Invoker):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| public class DrawCanvas extends Canvas implements Drawable { private Color color = Color.red; private int radius = 6; private MacroCommand history;
public DrawCanvas(int width, int height, MacroCommand history) { setSize(width, height); setBackground(Color.white); this.history = history; }
public void paint(Graphics g) { System.out.println("paint"); history.execute(); }
@Override public void draw(int x, int y) { Graphics g = getGraphics(); g.setColor(color); g.fillOval(x - radius, y - radius, radius * 2, radius * 2); }
}
|
四、命令模式有什么优缺点?
优点:
- 结构清晰,请求与接收解耦,命令之间相互隔离
- 扩展性好,很方便就能扩展新的命令
缺点:
- 容易产生大量的命令类,增加系统复杂度
- 请求和接收是分离的,可读性变差了,增加了代码理解上的难度