zoukankan      html  css  js  c++  java
  • 设计模式(二十二)Command模式

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

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

      Command有时也被称为时间。当发生点击鼠标、按下键盘按键等事件时,我们可以先将这些事件作成实例,然后按照发生顺序放入队列中。接着,再依次去处理它们。

      

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

    1 package bigjunoba.bjtu.command;
    2 
    3 public interface Command {
    4     public abstract void execute();
    5 }

      Command接口是表示“命令”的接口。作用就是“执行”什么东西。

    package bigjunoba.bjtu.command;
    
    import java.util.Stack;
    import java.util.Iterator;
    
    public class MacroCommand implements Command {
        // 命令的集合
        private Stack<Command> commands = new Stack<Command>();
        // 执行
        public void execute() {
            Iterator<Command> it = commands.iterator();
            while (it.hasNext()) {
                ((Command)it.next()).execute();
            }
        }
        // 添加命令,同时防止不小心将自己(this)添加进去。
        public void append(Command cmd) {
            if (cmd != this) {
                commands.push(cmd);
            }
        }
        // 删除push方法添加的最后一条命令
        public void undo() {
            if (!commands.empty()) {
                commands.pop();
            }
        }
        // 删除所有命令
        public void clear() {
            commands.clear();
        }
    }

      MacroCommand类表示“由多条命令整合成的命令”。该类实现了Command接口。commands字段是Stack类型的,它是保存了多个Command(即实现了Command接口的实例)的集合。

     1 package bigjunoba.bjtu.drawer;
     2 
     3 import bigjunoba.bjtu.command.*;
     4 import java.awt.Point;
     5 
     6 public class DrawCommand implements Command {
     7     // 绘制对象
     8     protected Drawable drawable;
     9     // 绘制位置
    10     private Point position;
    11     // 构造函数
    12     public DrawCommand(Drawable drawable, Point position) {
    13         this.drawable = drawable;
    14         this.position = position;
    15     }
    16     // 执行
    17     public void execute() {
    18         drawable.draw(position.x, position.y);
    19     }
    20 }

      DrawCommand类实现了Command接口,表示“绘制一个点的命令”。构造函数的作用是生成“在这个位置绘制点”的命令。execute方法的作用是执行命令。

    1 package bigjunoba.bjtu.drawer;
    2 
    3 public interface Drawable {
    4     public abstract void draw(int x, int y);
    5 }

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

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

      history字段中保存的是DrawCanvas类自己应当执行的绘制命令的集合。该字段是Command.MacroCommand类型的。当需要重新绘制DrawCanvas是,java处理会调用paint方法。它所做的事情仅仅是调用了history.excute方法。这样,记录在history中的所有历史命令都会被重新执行一遍。

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

      Main类实现了mouseDragged方法,当鼠标被拖动时,会生成一条“在这个位置画点”的命令,Command cmd = new DrawCommand(canvas, e.getPoint());该命令会先被添加至绘制历史记录中,history.append(cmd);然后立即执行,cmd.execute();

      测试结果如上图所示。

      示例程序的时序图。

      Command模式的类图。

  • 相关阅读:
    pandas基本操作
    MySQL数据库(五)使用pymysql对数据库进行增删改查
    MySQL数据库(四)多表查询
    MySQL数据库(三)索引总结
    MySQL数据库(二)-数据库的增删改查
    MySQL数据库(一)
    Linux 解压/压缩xxx.zip格式(unZip Zip的安装和使用)
    关系型数据库和非关系型数据库的对比
    进程、线程、协程的区别
    python迭代器与生成器详解
  • 原文地址:https://www.cnblogs.com/BigJunOba/p/8806546.html
Copyright © 2011-2022 走看看