一个类在进行生活时会调用自己或是其他类的方法,虽然调用结果会反映在对象的状态中,但并不会留下工作的历史记录。
这时,如果有一个类,用来表示“请进行这项工作”的命令就方便很多。每一项想做的工作就不再是“方法的调用”这种动态处理了,而是一个表示命令的类的实例,即可以用“物”来表示。要想管理工作的历史记录,只需管理这些实例的集合即可,而且还可以随意再次执行过去的命令,或是将多个过去的命令整合为一个新命令并执行。
Command有时也被称为时间。会把行为当做是事件,再将这些事件作为实例,然后按照发生顺序放入队列中,接着再依此去处理他们。
实例程序:
| 包 | 名字 | 说明 |
|---|---|---|
| command | Command | 表示“命令”的接口 |
| command | MacroCommand | 表示“由多条命令整合成的命令”的类 |
| drawer | DrawCommand | 表示“绘制一个点的命令”的类 |
| drawer | Drawable | 表示“绘制对象”的接口 |
| drawer | DrawCanvas | 实现“绘制对象”的类 |
| 无名 | Main | 测试程序行为的类 |
Command接口:
Command接口是表示“命令”的接口,在该接口只定义了一个方法,即execute(执行)。至于调用execute方法后具体会进行什么样的处理,则取决于实现了Command接口的类。总之,Command接口的作用就是“执行”什么东西。
public interface Command {public abstract void execute();}
MacroCommand类:
MacroCommand类表示“由多条命令整合成的命令”。该类实现了Command接口,MacroCommand中的Macro有“大量的”意思,一般表示“多条命令整合成的命令”。
command字段为栈。
public class MacroCommand implements Command {// 命令的集合private Stack commands = new Stack();// 执行@Overridepublic 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:
DrawCommand类实现了Command接口,表示“绘制一个点的命令”。
public class DrawCommand implements Command{// 绘制对象protected Drawable drawable;// 绘制位置private Point position;public DrawCommand(Drawable drawable, Point position) {this.drawable = drawable;this.position = position;}@Overridepublic void execute() {drawable.draw(position.x, position.y);}}
Drawable接口:
Drawable接口是表示“绘制对象”的接口。draw方法是用于绘制的方法。
public interface Drawable {public abstract void draw(int x, int y);}
DrawCanvas类:
在history字段中保存的额是DrawCanvas类自己应当执行的绘制命令的集合。该字段是MacroCommand类型的。
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();}@Overridepublic void draw(int x, int y) {Graphics g = getGraphics();g.setColor(color);g.fillOval(x - radius, y - radius, radius * 2, radius * 2);}}
Main类:
在history字段中保存的是绘制历史记录,会被传递给DrawCanvas的实例。canvas字段表示绘制区域。初始值为400乘400。clearButton字段是用于删除已绘制原点的按钮。JButton类是在javax.swing中定义的按钮类;
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){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接口中的方法@Overridepublic void actionPerformed(ActionEvent e) {if (e.getSource()==clearButton) {history.clear();canvas.repaint();}}// MouseMotionListener接口中的方法@Overridepublic void mouseDragged(MouseEvent e) {DrawCommand cmd = new DrawCommand(canvas, e.getPoint());history.append(cmd);cmd.execute();}@Overridepublic void mouseMoved(MouseEvent e) {}@Overridepublic void windowOpened(WindowEvent e) {}@Overridepublic void windowClosing(WindowEvent e) {System.exit(0);}@Overridepublic void windowClosed(WindowEvent e) {}@Overridepublic void windowIconified(WindowEvent e) {}@Overridepublic void windowDeiconified(WindowEvent e) {}@Overridepublic void windowActivated(WindowEvent e) {}@Overridepublic void windowDeactivated(WindowEvent e) {}public static void main(String[] args) {new Main("Command Pattern Sample");}}
Command模式中的登场角色:
Command(命令):
Command角色负责定义命令的接口,由Command接口扮演这个角色。
ConcreteCommand(具体的命令):
ConcreteCommand角色负责实现现在Command角色中定义的接口。
Received(接收者):
Received角色是Command角色执行命令时的对象,也被称作是命令接收者。
Client(请求者):
Client角色负责生成ConcreteCommand角色并分配Received角色。由Main类扮演此角色。
Invoker(发动者):
Invoker角色是开始执行命令的角色,会调用在Command角色中定义的接口。
拓展思路的要点:
命令中应该包含哪些信息:
关于“命令”中应该包含哪些信息这个问题,其实并没有绝对的答案,命令的目的不同,应该包含的信息也不同。DrawCommand类中包含了要绘制的点的位置信息,但不包含点的大小,颜色和形状等信息。
保存历史记录:
MacroCommand类的实例代表着绘制的历史记录。在该字段中保存了之前所有的绘制信息。
