设计模式:Command模式

Command模式——命令也是类

如果我们用一个类,来表示“进行某项工作”的命令,那么每一项工作就不在是“方法的调用”,而是一个表示命令的类的实例。如果想要管理工作的历史记录,只需要管理这些实例的集合即可,而且还可以随时再次执行过去的命令,或者是将过去的命令整合成一个新命令并执行。这就是Command模式。

Command有时也被称作事件。当发生鼠标,键盘按下事件时,我们可以先将这些事件作成实例,然后按照发生的顺序放入队列中。在需要处理事件的时候再按照顺序依次去处理它们。这种编程方式在GUI编程中经常遇到。

下面的示例程序是一个绘图软件,可以通过用户拖动鼠标来绘制点,还实现了颜色更改,撤销操作和清空绘图板等操作。

示例程序的运行结果如图。

这里写图片描述

示例程序的具体实现如下。

  • 类和接口的一览表
名字 说明
command Command 表示“命令”的接口
command MacroCommand 表示“由多条命令整合成的命令”的类
command DrawCommand 表示“绘制一个点的命令”的类
command ColorCommand 表示“改变颜色的命令”的类
drawer Drawable 表示“绘制对象”的接口
drawer DrawCanvas 表示“绘制对象”的类
default Main 测试程序行为的类
  • Command接口
package command;

public interface Command {
    public abstract void execute();
}
  • MacroCommand类
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.empty()) {
            commands.pop();
        }
    }

    public void clear() {
        commands.clear();
    }
}
  • DrawCommand类
package command;

import drawer.Drawable;

import java.awt.*;

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);
    }
}
  • ColorCommand类
package command;

import drawer.Drawable;

import java.awt.*;

public class ColorCommand implements Command {
    protected Drawable drawable;
    private Color color;

    public ColorCommand(Drawable drawable, Color color) {
        this.drawable = drawable;
        this.color = color;
    }

    @Override
    public void execute() {
        drawable.setColor(color);
    }
}
  • Drawable接口
package drawer;

import java.awt.*;

public interface Drawable {
    public abstract void init();

    public abstract void draw(int x, int y);

    public abstract void setColor(Color color);
}
  • DrawCanvas类
package drawer;

import command.ColorCommand;
import command.MacroCommand;

import java.awt.*;

public class DrawCanvas extends Canvas implements Drawable {
    private Color color;
    private int radius;
    private MacroCommand history;

    public DrawCanvas(int width, int height, MacroCommand history) {
        setSize(width, height);
        setBackground(Color.WHITE);
        this.history = history;
        init();
    }

    @Override
    public void paint(Graphics g) {
        history.execute();
    }

    @Override
    public void init() {
        color = Color.RED;
        radius = 6;
        history.append(new ColorCommand(this, color));
    }

    @Override
    public void draw(int x, int y) {
        Graphics g = getGraphics();
        g.setColor(color);
        g.fillOval(x - radius, y - radius, radius * 2, radius * 2);
    }

    @Override
    public void setColor(Color color) {
        this.color = color;
    }
}
  • Main类
import command.ColorCommand;
import command.Command;
import command.DrawCommand;
import command.MacroCommand;
import drawer.DrawCanvas;

import javax.swing.*;
import java.awt.*;
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");
    private JButton undoButton = new JButton("Undo");
    private JButton redButton  = new JButton("red");
    private JButton greenButton  = new JButton("green");
    private JButton blueButton  = new JButton("blue");

    public Main(String title) {
        super(title);
        this.addWindowListener(this);
        canvas.addMouseMotionListener(this);
        clearButton.addActionListener(this::actionPerformed);
        undoButton.addActionListener(this::actionPerformed);
        redButton.addActionListener(this::actionPerformed);
        greenButton.addActionListener(this::actionPerformed);
        blueButton.addActionListener(this::actionPerformed);
        Box buttonBox = new Box(BoxLayout.X_AXIS);
        buttonBox.add(redButton);
        buttonBox.add(greenButton);
        buttonBox.add(blueButton);
        buttonBox.add(clearButton);
        buttonBox.add(undoButton);
        Box mainBox = new Box(BoxLayout.Y_AXIS);
        mainBox.add(buttonBox);
        mainBox.add(canvas);
        getContentPane().add(mainBox);
        pack();
        setVisible(true);
    }


    @Override
    public void actionPerformed(ActionEvent e) {
        if (e.getSource() == clearButton) {
            history.clear();
            canvas.repaint();
        } else if (e.getSource() == undoButton) {
            history.undo();
            canvas.repaint();
        }else if (e.getSource() == redButton) {
            Command cmd = new ColorCommand(canvas, Color.red);
            history.append(cmd);
            cmd.execute();
        } else if (e.getSource() == greenButton) {
            Command cmd = new ColorCommand(canvas, Color.green);
            history.append(cmd);
            cmd.execute();
        } else if (e.getSource() == blueButton) {
            Command cmd = new ColorCommand(canvas, Color.blue);
            history.append(cmd);
            cmd.execute();
        }
    }

    @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 windowClosing(WindowEvent e) {
        System.exit(0);
    }

    @Override
    public void windowOpened(WindowEvent e) {}
    @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");
    }
}

Command模式中的角色

  • Command(命令)

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

  • ConcreteCommand(具体的命令)

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

  • Receiver(接受者)

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

  • Client(请求者)

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

  • Invoker(发动者)

Invoker角色是开始执行命令的角色,它会调用Command角色中定义的接口(API)。在示例程序中,由Main类和DrawCanvas类扮演此角色。


Command模式的思路

  • 确定命令中应包含的信息。根据命令的目的不同,应该包含的信息也不同。

  • 可以通过对象序列化把绘制的历史命令保存成文件,永久保存历史记录。


想了解更多关于设计模式:设计模式专栏

猜你喜欢

转载自blog.csdn.net/qq_39384184/article/details/80760931
今日推荐