• 欢迎访问 winrains 的个人网站!
  • 本网站主要从互联网整理和收集了与Java、网络安全、Linux等技术相关的文章,供学习和研究使用。如有侵权,请留言告知,谢谢!

图解设计模式(22):Command模式(命令也是类)

设计模式 winrains 1年前 (2019-09-24) 58次浏览

用一个类来表示“请进行这项工作”的命令,每一项想做的工作就不再是“方法的调用”,而是一个表示命令的类的实例。要想管理工作的历史记录,只需要管理这些实例的集合即可,而且还可以随时再次执行过去的命令,或是将多个过去的命令整合为一个新命令并执行。
在设计模式中,称这样的“命令”为Command模式。
Command有时也被称为事件(event)。当发生点击鼠标、按下键盘等事件时,可以先将这些事件作成实例,然后按照发生顺序放入队列中。接着,再位次去处理它们。

1 Command模式中的角色

  • Command(命令)

负责定义命令的接口。在示例中,对应Command接口。

  • ConcreteCommand(具体的命令)

负责实现在Command角色中定义的接口。在示例中,对应MacroCommand类和DrawCommand类。

  • Receiver(接收者)

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

  • Client(请求者)

负责生成ConcreteCommand角色并分配Receiver角色。在示例中,对应Main类。在响应鼠标拖拽事件时,它生成了DrawCommand类的实例,并将扮演Receiver角色的DrawCanvas类的实例传递给了DrawCommand类的构造方法。

  • Invoker(发动者)

开始执行命令的角色,它会调用在Command角色中定义的接口。在示例中,对应Main类和DrawCanvas类。Main类同时扮演了Client角色和Invoker角色。

2 Command模式的类图

3 示例程序

该示例程序是一个画图软件,当用户拖动鼠标时程序会绘制出红色圆点,点击clear按钮后会清除所有的圆点。
用户每拖动一次鼠标,应用程序都会为“在这个位置画一个点“这条命令生成一个DrawCommand类的实例。只要保存了这条命令,以后有需要时就可以重新绘制。

3.1 类和接口一览表

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

3.2 类图

3.3 示例代码

Command接口

表示“命令“的接口。在该接口中只定义了一个execute方法。

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

MacroCommand类

表示“由多条命令整合成的命令“,实现了Command接口。
append方法新增加的Command也可能是MacroCommand类的实例,需要将自己排除在外,否则会陷入死循环,永远不停地执行。

package command;
import java.util.Stack;
import java.util.Iterator;
public class MacroCommand implements Command {
    // 命令的集合
    private Stack commands = new Stack();
    // 执行
    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.empty()) {
            commands.pop();
        }
    }
    // 删除所有命令
    public void clear() {
        commands.clear();
    }
}

DrawCommand类

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

package drawer;
import command.Command;
import java.awt.Point;
public class DrawCommand implements Command {
    // 绘制对象
    protected Drawable drawable;
    // 绘制位置
    private Point position;
    // 构造函数
    public DrawCommand(Drawable drawable, Point position) {
        this.drawable = drawable;
        this.position = position;
    }
    // 执行
    public void execute() {
        drawable.draw(position.x, position.y);
    }
}

Drawable接口

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

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

DrawCanvas类

实现了Drawable接口,是java.awt.Canvas的子类。
history字段中保存的是DrawCanvas类自己应当执行的绘制命令的集合。

package drawer;
import command.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
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();
    }
    // 绘制
    public void draw(int x, int y) {
        Graphics g = getGraphics();
        g.setColor(color);
        g.fillOval(x - radius, y - radius, radius * 2, radius * 2);
    }
}

Main类

测试程序。
history字段中保存的是绘制历史记录。

import command.*;
import drawer.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
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();
    }
    // ActionListener接口中的方法
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == clearButton) {
            history.clear();
            canvas.repaint();
        }
    }
    // MouseMotionListener接口中的方法
    public void mouseMoved(MouseEvent e) {
    }
    public void mouseDragged(MouseEvent e) {
        Command cmd = new DrawCommand(canvas, e.getPoint());
        history.append(cmd);
        cmd.execute();
    }
    // WindowListener接口中的方法
    public void windowClosing(WindowEvent e) {
        System.exit(0);
    }
    public void windowActivated(WindowEvent e) {}
    public void windowClosed(WindowEvent e) {}
    public void windowDeactivated(WindowEvent e) {}
    public void windowDeiconified(WindowEvent e) {}
    public void windowIconified(WindowEvent e) {}
    public void windowOpened(WindowEvent e) {}
    public static void main(String[] args) {
        new Main("Command Pattern Sample");
    }
}

运行结果

摘自《图解设计模式》


版权声明:文末如注明作者和来源,则表示本文系转载,版权为原作者所有 | 本文如有侵权,请及时联系,承诺在收到消息后第一时间删除 | 如转载本文,请注明原文链接。
喜欢 (1)