【图解设计模式】Command模式

用对象表示“命令”来保存命令历史记录和重复执行命令。

示例

一个画图软件,用户拖动鼠标时程序会绘制出红色圆点,点击clear按钮后会清除所有的圆点。

类图

mXy37V.png

时序图

mXyg9e.png

Command接口

1
2
3
4
5
package command;

public interface Command {
public abstract void execute();
}

MacroCommand类

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
29
30
package command;

import java.util.Iterator;
import java.util.Stack;

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();
}
}

DrawCommand类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package drawer;

import command.Command;

import java.awt.*;

public class DrawCommand implements Command {
private 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);
}
}

Drawable接口

1
2
3
4
5
package drawer;

public interface Drawable {
public abstract void draw(int x, int y);
}

DrawCanvas类

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
package drawer;

import command.MacroCommand;

import java.awt.*;

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) {
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);
}
}

Main类

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import command.Command;
import command.MacroCommand;
import drawer.DrawCanvas;
import drawer.DrawCommand;

import javax.swing.*;
import java.awt.event.*;

public class Main extends JFrame implements ActionListener, MouseMotionListener, WindowListener {
private MacroCommand history = new MacroCommand();
private DrawCanvas canvas = new DrawCanvas(400, 400, history);
private JButton clearButton = new JButton("clear");

public Main(String title) {
super(title);

this.addWindowListener(this);
canvas.addMouseMotionListener(this);
clearButton.addActionListener(this);

Box buttonBox = new Box(BoxLayout.X_AXIS);
buttonBox.add(clearButton);
Box mainBox = new Box(BoxLayout.Y_AXIS);
mainBox.add(buttonBox);
mainBox.add(canvas);
getContentPane().add(mainBox);

pack();
show();
}

@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == clearButton) {
history.clear();
canvas.repaint();
}
}

@Override
public void mouseDragged(MouseEvent e) {
Command cmd = new DrawCommand(canvas, e.getPoint());
history.append(cmd);
cmd.execute();
}

@Override
public void mouseMoved(MouseEvent e) {}

@Override
public void windowOpened(WindowEvent e) {}

@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}

@Override
public void windowClosed(WindowEvent e) {}

@Override
public void windowIconified(WindowEvent e) {}

@Override
public void windowDeiconified(WindowEvent e) {}

@Override
public void windowActivated(WindowEvent e) {}

@Override
public void windowDeactivated(WindowEvent e) {}

public static void main(String[] args) {
new Main("Command Pattern Sample");
}
}

运行结果

mjMRWF.png

登场角色

Command(命令)

Command角色负责定义命令的接口(API)。在示例程序中,由Command接口扮演此角色。

ConcreteCommand(具体的命令)

ConcreteCommand角色负责实现在Command角色中定义的接口(API)。在示例程序中,由MacroCommand类和DrawCommand类扮演此角色。

Receiver(接受者)

Receiver角色是Command角色执行命令时的对象,也可以称其为命令接收者。在示例程序中,由DrawCanvas类接收DrawCommand的命令。

Client(请求者)

Client角色负责生成ConcreteCommand角色并分配Receiver角色。在示例程序中,由Main类扮演此角色。在相应鼠标拖拽事件时,它生成了DrawCommand类的实例,并将扮演Receiver角色的DrawCanvas类的实例传递给了DrawCommand类的构造函数。

Invoker(发动者)

Invoker角色是开始执行命令的角色,它会调用在Command角色中定义的接口(API)。在示例程序中,由Main类和DrawCanvas类扮演此角色。这两个类都调用了Command接口中的execute方法。Main类同时扮演了Client角色和Invoker角色。

类图

mjUTi9.png

时序图

mjaKWn.png