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

示例程序结构:

抽象命令接口(Command):
| 12
 3
 
 | public interface Command {void execute();
 }
 
 | 
具体的命令(ConcreteCommand):
| 12
 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);
 }
 }
 
 | 
| 12
 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):
| 12
 3
 
 | public interface Drawable {void draw(int x, int y);
 }
 
 | 
命令发动者(Invoker):
| 12
 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);
 }
 
 }
 
 | 
四、命令模式有什么优缺点?
优点:
- 结构清晰,请求与接收解耦,命令之间相互隔离
- 扩展性好,很方便就能扩展新的命令
缺点:
- 容易产生大量的命令类,增加系统复杂度
- 请求和接收是分离的,可读性变差了,增加了代码理解上的难度