Author:Gorit
Date:2021年11月28日
Refer:《图解设计模式》

22.1 Command 模式

一个类在进行工作时会调用自己 伙食 其他类的方法,虽然调用的结果会反映在对象的状态中,但并不会留下工作的历史记录

假如,有这样一个类,用来表示“请进行这项工作”的“命令”就会方便很多。每一项想做的工作就不再是“方法的调用”这种动态处理了,而是一个表示命令的类的实例,即可用“物”来表示,需要管理工作记录的历史记录。只需要管理这些实例的集合即可,还可以随时再次执行过去的命令,或是将多个过去的命令整合为一个新命令并执行

在设计模式中,我们称这样的“命令”为 Command 模式

22.2 示例程序

我们将做一个绘图的程序,点击保存,可以实现绘制固定图形发的操作(因为保存了记录)

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

Command

  1. package Commad.command;
  2. /**
  3. * @Author Gorit
  4. * @Date 2021/11/28
  5. * 执行操作
  6. **/
  7. public interface Command {
  8. public abstract void execute();
  9. }

MacroCommand

  1. package Commad.command;
  2. import java.util.Iterator;
  3. import java.util.Stack;
  4. /**
  5. * @Author Gorit
  6. * @Date 2021/11/28
  7. **/
  8. public class MacroCommand implements Command {
  9. // 命令的集合
  10. private Stack commands = new Stack();
  11. public void execute() {
  12. Iterator it = commands.iterator();
  13. while (it.hasNext()) {
  14. ((Command)it.next()).execute();
  15. }
  16. }
  17. // 添加命令
  18. public void append(Command cmd) {
  19. if (cmd != this) {
  20. commands.push(cmd);
  21. }
  22. }
  23. // 撤销最后一个命令
  24. public void undo() {
  25. if (!commands.empty()) {
  26. commands.pop();
  27. }
  28. }
  29. // 删除所有命令
  30. public void clear() {
  31. commands.clear();
  32. }
  33. }

DrawPointCommand

  1. package Commad.drawer;
  2. import Commad.command.Command;
  3. import java.awt.*;
  4. /**
  5. * @Author Gorit
  6. * @Date 2021/11/28
  7. * 实现绘制一个点的功能
  8. **/
  9. public class DrawPointCommand implements Command {
  10. // 绘制的对象
  11. protected Drawable drawable;
  12. // 绘制位置
  13. private Point position;
  14. public DrawPointCommand(Drawable drawable, Point position) {
  15. this.drawable = drawable;
  16. this.position = position;
  17. }
  18. public void execute() {
  19. drawable.draw(position.x, position.y);
  20. }
  21. }

Drawable

  1. package Commad.drawer;
  2. /**
  3. * @Author Gorit
  4. * @Date 2021/11/28
  5. * 提供绘制的接口
  6. **/
  7. public interface Drawable {
  8. public abstract void draw(int x, int y);
  9. }

DrawCanvas

  1. package Commad.drawer;
  2. import Commad.command.MacroCommand;
  3. import java.awt.*;
  4. /**
  5. * @Author Gorit
  6. * @Date 2021/11/28
  7. **/
  8. public class DrawCanvas extends Canvas implements Drawable {
  9. // 颜色
  10. private Color color = Color.red;
  11. // 绘制的圆点半径
  12. private int radius = 6;
  13. // 命令的历史记录
  14. private MacroCommand history;
  15. public DrawCanvas(int width, int height, MacroCommand history) {
  16. setSize(width, height);
  17. setBackground(Color.white);
  18. this.history = history;
  19. }
  20. // 重新全部绘制
  21. public void paint(Graphics q) {
  22. history.execute();
  23. }
  24. // 绘制
  25. public void draw(int x, int y) {
  26. Graphics g = getGraphics();
  27. g.setColor(color);
  28. g.fillOval(x - radius, y - radius,radius * 2, radius * 2);
  29. }
  30. }

Main

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

image.png

22.3 Command 中登场的角色

一、Command(命令)

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

二、ConcreateCommand(具体的命令)

ConcreateCommand 角色负责实现在 Command 角色中定义的接口(API)。
由 MacroCommand 类 和 DrawCommand 类扮演此角色

三、Receiver(接受者)

Receiver 角色是 Command 角色执行命令时的对象,也可以成其为命令的接受者。
由 DrawCanvas 类 接收 DrawCommand 的命令。

四、Client(请求者)

Client 角色负责生成 ConcreateCommand 角色并分配 Receiver 角色。
由 Main 扮演此角色

鼠标响应拖拽事件时,它生成了 DrawCommand 类的实例,并将扮演 Receiver 角色的 DrawCanvas 类的实例传递给了 DrawCommand 类的构造函数

五、Invoker(发动者)

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

22.4 相关的设计模式

一、Composite 模式

有时会使用 Composite 模式实现宏命令

二、Memento 模式

有时会使用 Memento 模式来保存角色的历史记录

三、Protype 模式

有时会使用 Protype 模式复制发生的事件(生成的命令)