一个类在进行生活时会调用自己或是其他类的方法,虽然调用结果会反映在对象的状态中,但并不会留下工作的历史记录。
这时,如果有一个类,用来表示“请进行这项工作”的命令就方便很多。每一项想做的工作就不再是“方法的调用”这种动态处理了,而是一个表示命令的类的实例,即可以用“物”来表示。要想管理工作的历史记录,只需管理这些实例的集合即可,而且还可以随意再次执行过去的命令,或是将多个过去的命令整合为一个新命令并执行。
Command有时也被称为时间。会把行为当做是事件,再将这些事件作为实例,然后按照发生顺序放入队列中,接着再依此去处理他们。

实例程序:

名字 说明
command Command 表示“命令”的接口
command MacroCommand 表示“由多条命令整合成的命令”的类
drawer DrawCommand 表示“绘制一个点的命令”的类
drawer Drawable 表示“绘制对象”的接口
drawer DrawCanvas 实现“绘制对象”的类
无名 Main 测试程序行为的类

Command接口:

Command接口是表示“命令”的接口,在该接口只定义了一个方法,即execute(执行)。至于调用execute方法后具体会进行什么样的处理,则取决于实现了Command接口的类。总之,Command接口的作用就是“执行”什么东西。

  1. public interface Command {
  2. public abstract void execute();
  3. }

MacroCommand类:

MacroCommand类表示“由多条命令整合成的命令”。该类实现了Command接口,MacroCommand中的Macro有“大量的”意思,一般表示“多条命令整合成的命令”。
command字段为栈。

  1. public class MacroCommand implements Command {
  2. // 命令的集合
  3. private Stack commands = new Stack();
  4. // 执行
  5. @Override
  6. public void execute() {
  7. Iterator it = commands.iterator();
  8. while (it.hasNext()) {
  9. ((Command) it.next()).execute();
  10. }
  11. }
  12. // 添加命令
  13. public void append(Command cmd) {
  14. if (cmd != this) {
  15. commands.push(cmd);
  16. }
  17. }
  18. // 删除最后一条命令
  19. public void undo() {
  20. if (!commands.empty()) {
  21. commands.pop();
  22. }
  23. }
  24. // 删除所有命令
  25. public void clear() {
  26. commands.clear();
  27. }
  28. }

DrawCommand:

DrawCommand类实现了Command接口,表示“绘制一个点的命令”。

  1. public class DrawCommand implements Command{
  2. // 绘制对象
  3. protected Drawable drawable;
  4. // 绘制位置
  5. private Point position;
  6. public DrawCommand(Drawable drawable, Point position) {
  7. this.drawable = drawable;
  8. this.position = position;
  9. }
  10. @Override
  11. public void execute() {
  12. drawable.draw(position.x, position.y);
  13. }
  14. }

Drawable接口:

Drawable接口是表示“绘制对象”的接口。draw方法是用于绘制的方法。

  1. public interface Drawable {
  2. public abstract void draw(int x, int y);
  3. }

DrawCanvas类:

在history字段中保存的额是DrawCanvas类自己应当执行的绘制命令的集合。该字段是MacroCommand类型的。

  1. public class DrawCanvas extends Canvas implements Drawable {
  2. // 颜色
  3. private Color color = Color.red;
  4. // 要绘制的圆点的半径
  5. private int radius = 6;
  6. // 命令的历史记录
  7. private MacroCommand history;
  8. // 构造函数
  9. public DrawCanvas(int width, int height, MacroCommand history) {
  10. setSize(width, height);
  11. setBackground(Color.white);
  12. this.history = history;
  13. }
  14. // 重新全部绘制
  15. public void paint(Graphics g) {
  16. history.execute();
  17. }
  18. @Override
  19. public void draw(int x, int y) {
  20. Graphics g = getGraphics();
  21. g.setColor(color);
  22. g.fillOval(x - radius, y - radius, radius * 2, radius * 2);
  23. }
  24. }

Main类:

在history字段中保存的是绘制历史记录,会被传递给DrawCanvas的实例。canvas字段表示绘制区域。初始值为400乘400。clearButton字段是用于删除已绘制原点的按钮。JButton类是在javax.swing中定义的按钮类;
Main类的构造函数中设置了用于接收鼠标按下等事件的监听器,并安排了各个控制在界面中的布局。

  1. public class Main extends JFrame implements ActionListener, MouseMotionListener, WindowListener {
  2. // 绘制的历史记录
  3. private MacroCommand history = new MacroCommand();
  4. // 绘制区域
  5. private DrawCanvas canvas = new DrawCanvas(400, 400, history);
  6. // 删除按钮
  7. private JButton clearButton = new JButton("clear");
  8. // 构造函数
  9. public Main(String title){
  10. super(title);
  11. this.addWindowListener(this);
  12. canvas.addMouseMotionListener(this);
  13. clearButton.addActionListener(this);
  14. Box buttonBox = new Box(BoxLayout.X_AXIS);
  15. buttonBox.add(clearButton);
  16. Box mainBox = new Box(BoxLayout.Y_AXIS);
  17. mainBox.add(buttonBox);
  18. mainBox.add(canvas);
  19. getContentPane().add(mainBox);
  20. pack();
  21. show();
  22. }
  23. // ActionListener接口中的方法
  24. @Override
  25. public void actionPerformed(ActionEvent e) {
  26. if (e.getSource()==clearButton) {
  27. history.clear();
  28. canvas.repaint();
  29. }
  30. }
  31. // MouseMotionListener接口中的方法
  32. @Override
  33. public void mouseDragged(MouseEvent e) {
  34. DrawCommand cmd = new DrawCommand(canvas, e.getPoint());
  35. history.append(cmd);
  36. cmd.execute();
  37. }
  38. @Override
  39. public void mouseMoved(MouseEvent e) {
  40. }
  41. @Override
  42. public void windowOpened(WindowEvent e) {
  43. }
  44. @Override
  45. public void windowClosing(WindowEvent e) {
  46. System.exit(0);
  47. }
  48. @Override
  49. public void windowClosed(WindowEvent e) {
  50. }
  51. @Override
  52. public void windowIconified(WindowEvent e) {
  53. }
  54. @Override
  55. public void windowDeiconified(WindowEvent e) {
  56. }
  57. @Override
  58. public void windowActivated(WindowEvent e) {
  59. }
  60. @Override
  61. public void windowDeactivated(WindowEvent e) {
  62. }
  63. public static void main(String[] args) {
  64. new Main("Command Pattern Sample");
  65. }
  66. }

Command模式中的登场角色:

Command(命令):

Command角色负责定义命令的接口,由Command接口扮演这个角色。

ConcreteCommand(具体的命令):

ConcreteCommand角色负责实现现在Command角色中定义的接口。

Received(接收者):

Received角色是Command角色执行命令时的对象,也被称作是命令接收者。

Client(请求者):

Client角色负责生成ConcreteCommand角色并分配Received角色。由Main类扮演此角色。

Invoker(发动者):

Invoker角色是开始执行命令的角色,会调用在Command角色中定义的接口。

拓展思路的要点:

命令中应该包含哪些信息:

关于“命令”中应该包含哪些信息这个问题,其实并没有绝对的答案,命令的目的不同,应该包含的信息也不同。DrawCommand类中包含了要绘制的点的位置信息,但不包含点的大小,颜色和形状等信息。

保存历史记录:

MacroCommand类的实例代表着绘制的历史记录。在该字段中保存了之前所有的绘制信息。