命令模式

99 阅读2分钟

背景

如果我们有一个类,用来表示“请进行这项工作”的“命令”就会方便很多。每一项想做的工作就不再是“方法的调用”这种动态处理了,而是一个表示命令的类的实例,即可以用“物”来表示。

这样的“命令”称为 Command 模式

Command 有时也被称为事件,它与“事件驱动编程”中的“事件”是一样的意思。当发生点击鼠标、按下键盘按键等事件时,我们可以先将这些事件作成实例,然后按照发生顺序放入队列中。

登场角色

Command 命令

负责定义命令的API;

ConcreteCommand 具体的命令

实现Command 角色 中定义的 API;

Receiver 接收者

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

Client 请求者

Client 角色负责生成 ConcreteCommand 角色分配 Receiver 角色

Invoker 发动者

Invoker 角色是开始执行命令的角色,他会调用在Command 角色中定义的接口(API),即调用Command 类中的 excute 方法

类图

示例代码

示例代码为一个绘图程序,用命令模式来记录以往的路径

Command

public interface Command {
    void execute();
}

MacroCommand:记录命令的集合

public class MacroCommand implements Command {

    private Stack<Command> commands = new Stack<>();

    // 执行
    @Override
    public void execute() {
        if (!commands.isEmpty()) {
            Iterator<Command> it = commands.iterator();
            while (it.hasNext()) {
                Command command = it.next();
                command.execute();
            }
        }
    }

    // 添加命令
    public void addCommand(Command command) {
        if (command != this)
            commands.push(command);
    }

    // 撤销命令
    public void undo() {

        if (!commands.isEmpty()) {
            commands.pop();
        }
    }

    // 清除所有的命令
    public void clear() {
        commands.clear();
    }
}

DrawCommand:具体的命令

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

Drawable:具体画图的接口

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

DrawCanvas:相当于Receiver

public class DrawCanvas extends Canvas implements Drawable{
    private Color color = Color.red;

    private int radius = 6;

    // 保存历史记录
    private MacroCommand history;

    public DrawCanvas(int width, int heigth, MacroCommand history) {
        setSize(width, heigth);
        setBackground(Color.white);
        this.history = history;
    }

    // 重绘历史记录
    @Override
    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:启动类

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) throws HeadlessException {
        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(clearButton);
        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 command = new DrawCommand(canvas, e.getPoint());
        history.addCommand(command);
        command.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");
    }
}

功能分析

  1. 可以使用一个集合来保存历史命令,从而代表了记录历史记录;