设计模式:命令(Command)模式

一、前言

命令也是类,将命令作为一个类来保存,当要使用的时候可以直接拿来使用,比如脚本语言写出的脚本,只需要一个命令就能执行得到我们想要的需要操作很长时间才能得到的结果。这是一个非常有意思的模式,将操作的步骤保存下来,本例之中我们使用java自带的GUI来画图,然后将画图的过程(在哪个地方画了什么东西)保存下来,可以把每一次我们的操作作为一个命令,其实就是<使用什么画布,画点的坐标>,将这个命令对应的对象保存到所有命令对象的集合之中去,这样命令集合就记录下来了每一个命令,如果要显示画的内容的时候,直接将这些命令组合读取出来在进行一次重画即可。通过这种模式保存下来已经执行的步骤,通过重画再复述出来,是一种非常重要的开发理念,在需要保存历史纪录并恢复的场合是非常有用的。

二、代码

 Command接口:

 package zyr.dp.command;

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

 DrawCommand类:

 package zyr.dp.command;

 import java.awt.Point;

 public class DrawCommand implements Command {

     private 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);
} }
MacroCommand 类:
 package zyr.dp.command;

 import java.util.Iterator;
import java.util.Stack; public class MacroCommand implements Command { Stack commands=new Stack(); public void execute() {
Iterator it = commands.iterator();
while(it.hasNext()){
Command command=(Command)it.next();
command.execute();
}
} public void append(Command command){
if(command!=this){
commands.add(command);
}
} public void clear(){
commands.clear();
} public void undo(){
if(!commands.isEmpty()){
commands.pop();
}
} }

 Drawable接口:

 package zyr.dp.command;

 public interface Drawable {

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

 }
DrawCanvas 实现类:
 package zyr.dp.command;

 import java.awt.*;
import java.util.Random; public class DrawCanvas extends Canvas implements Drawable { private static final long serialVersionUID = 1972130370393242746L; private MacroCommand history;
private int radius=8; public DrawCanvas(int width,int hieght, MacroCommand history){
setSize(width,hieght);
setBackground(Color.white);
this.history=history;
} public void draw(int x, int y) {
Random random = new Random(); Graphics g = getGraphics();
g.setColor((random.nextBoolean())? Color.yellow : Color.MAGENTA);
g.fillOval(x-radius, y-radius, radius*2, radius*2);
} @Override
public void paint(Graphics g) {
System.out.println("执行一次刷新!"+System.currentTimeMillis());
history.execute();
} }

  Main类:

 package zyr.dp.command;

 import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener; import javax.swing.*; public class Main extends JFrame implements ActionListener,MouseMotionListener,WindowListener{ private MacroCommand history=new MacroCommand() ; private JButton btnClear=new JButton("清除");
private JButton btnRePaint=new JButton("重现"); private DrawCanvas canvas=new DrawCanvas(400,400,history); public Main(String title){
super(title); this.addWindowListener(this);
canvas.addMouseMotionListener(this);
btnClear.addActionListener(this);
btnRePaint.addActionListener(this); Box btnBox=new Box(BoxLayout.X_AXIS);
btnBox.add(btnClear);
btnBox.add(btnRePaint); Box mainBox=new Box(BoxLayout.Y_AXIS);
mainBox.add(btnBox);
mainBox.add(canvas); getContentPane().add(mainBox); pack();
show();
} public static void main(String[] args) { new Main("命令模式"); } @Override
public void actionPerformed(ActionEvent e) {
if(e.getSource()==btnClear){
history.clear();
canvas.repaint();
}else if(e.getSource()==btnRePaint){
canvas.repaint();
}
} @Override
public void mouseDragged(MouseEvent e) {
Command cmd=new DrawCommand(canvas,e.getPoint());
history.append(cmd);
cmd.execute();
} @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) {
} @Override
public void mouseMoved(MouseEvent e) {
}
}

实验结果:

  由此我们可以看到保存了的命令就这样一个个的再次执行了一遍,是不是很有意思呢?!

  让我们分析一下程序执行的过程:

  1、开始执行初始化界面,然后显示:

     public static void main(String[] args) {

         new Main("命令模式");

}
     public Main(String title){
super(title); this.addWindowListener(this);
canvas.addMouseMotionListener(this);
btnClear.addActionListener(this);
btnRePaint.addActionListener(this); Box btnBox=new Box(BoxLayout.X_AXIS);
btnBox.add(btnClear);
btnBox.add(btnRePaint); Box mainBox=new Box(BoxLayout.Y_AXIS);
mainBox.add(btnBox);
mainBox.add(canvas); getContentPane().add(mainBox); pack();
show();
}

   2、然后等待用户的操作,当监听到用户在界面上拖动鼠标的时候,执行:

     @Override
public void mouseDragged(MouseEvent e) {
Command cmd=new DrawCommand(canvas,e.getPoint());
history.append(cmd);
cmd.execute();
}

  3、创建一个命令对象,然后记录进命令堆栈之中,之后我们跟踪 cmd.execute();

 package zyr.dp.command;

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

4、这里就看到我们的面向抽象编程的好处了,根本不需要知道是谁执行了我们的命令,在命令的时候自然知道了,那就是new DrawCommand(canvas,e.getPoint());我们继续跟踪:

 public class DrawCommand implements Command {

          。。。
public void execute() {
drawable.draw(position.x, position.y);
} }

  5、继续跟踪:

 package zyr.dp.command;

 public interface Drawable {

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

}

  6、同理,谁实现了Drawable ,并被传递进去了,Command cmd=new DrawCommand(canvas,e.getPoint());

 private DrawCanvas canvas=new DrawCanvas(400,400,history);

   找到原主:DrawCanvas ,跟踪:

     public void draw(int x, int y) {
Random random = new Random(); Graphics g = getGraphics();
g.setColor((random.nextBoolean())? Color.yellow : Color.MAGENTA);
g.fillOval(x-radius, y-radius, radius*2, radius*2);
}

   因此执行我们的程序,画了一个点。之后我们的鼠标不断拖动着,这个流程就一直执行着,直到我们停止为止。

  之后我们分析重画方法:

   当用户点击按钮:

     @Override
public void actionPerformed(ActionEvent e) {
if(e.getSource()==btnClear){
history.clear();
canvas.repaint();
}else if(e.getSource()==btnRePaint){
canvas.repaint();
}
}

  调用 canvas.repaint();方法,这是Canvas自动实现的,我们不必深究,只需要知道这个函数之中会调用,我们的继承了Canvas并且重写的方法:

     public void paint(Graphics g) {
System.out.println("执行一次刷新!"+System.currentTimeMillis());
history.execute();
}

   跟踪: history.execute();

     public void execute() {
Iterator it = commands.iterator();
while(it.hasNext()){
Command command=(Command)it.next();
command.execute();
}
}

  可以看到将保存的命令一个个都拿出来,重新走了一遍我们上面的command.execute();所走的流程,这就是命令模式,现在很清晰了。

三、总结

对于命令模式,在本例之中使用了Composite模式,迭代器等模式作为辅助,另外在生成对象的时候还可能使用原型模式,在保存命令的时候还可能使用备忘录模式。本例是一个很好的例子,从本质上说明了命令模式就是将命令抽象成一个类,通过保存接收者的引用,在后期还可以让接收者去执行,同样的使用了组合模式将这些对象一个个的保存了下来,然后一步步的调用单个命令的执行方法,该执行方法通知命令的接收者去再次执行命令,这种方式特别的方便,因为我们保存的是用户的操作,能够一直记录下来,甚至可以保存到文件之中以后可以恢复,由此可以看到命令模式的强大。

程序代码

设计模式:命令(Command)模式的更多相关文章

  1. junit设计模式--命令者模式

    命令模式的意图 将一个请求封装成一个对象,从而使你可以用不同的请求对客户进行参数化: 对请求排队或记录请求日志,以及支持可撤销的操作: 命令模式告诉我们可以为一个操作生成一个对象并给出它的一个执行方法 ...

  2. 设计模式C++描述----19.命令(Command)模式

    一. 举例说明 我们知道,在多线程程序中,多个用户都给系统发 Read 和 Write 命令.这里有几点需要说明: 1. 首先明确一点,所有的这些 Read 和 Write 命令都是调用一个库函数. ...

  3. 命令(Command)模式

    命令模式又称为行动(Action)模式或者交易(Transaction)模式. 命令模式把一个请求或者操作封装到一个对象中.命令模式允许系统使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可 ...

  4. 设计模式 命令-Command

    命令-Command 当要向不同类的对象发出相同的请求时,可以将接收者和他的动作封装进一个命令对象.这样调用者只和命令产生依赖.而不会和众多的接收者发生依赖. Head First例子 要设计一款遥控 ...

  5. python 设计模式之命令(Command)模式

    #写在前面 也了解了不少设计模式了,他们都有一个通病,那就是喜欢把简单的东西复杂化.比如在不同的类中加个第三者.哈哈哈,简单变复杂是有目的的,那就是降低耦合度,增强可维护性,提高代码复用性,使代码变得 ...

  6. 十五、命令(Command)模式--行为型模式(Behavioral Pattern)

    命令模式又称为行动(Action)模 式或交易(Transaction)模式.命令模式把一个请求或者操作封装到一个对象中. 命令模式是对命令的封装.命令模式把发出命令的责任和执行命令的责任分割开,委派 ...

  7. 设计模式:command模式

    目的:将命令设计成类的形式,并可以组织成队列 优点: 在需要的情况下,可以比较容易地将命令记入日志 可以容易的实现对请求的撤销和重做 由于新的具体命令类不影响其他的命令类,因此增加新的具体命令类很容易 ...

  8. 设计模式--命令模式(Command)

    基本概念:  Command模式也叫命令模式 ,是行为设计模式的一种.Command模式通过被称为Command的类封装了对目标对象的调用行为以及调用参数,命令模式将方法调用给封装起来了. 命令模式的 ...

  9. 设计模式---行为变化模式之命令模式(Command)

    前提:行为变化模式 在组件的构建过程中,组建行为的变化经常导致组件本身剧烈的变化.“行为变化”模式将组件的行为和组件本身进行解耦,从而支持组件的变化,实现两者之间的松耦合. 类中非虚函数和静态函数方法 ...

  10. Java设计模式(22)命令模式(Command模式)

    Command模式是最让我疑惑的一个模式,我在阅读了很多代码后,才感觉隐约掌握其大概原理,我认为理解设计模式最主要是掌握起原理构造,这样才对自己实际编程有指导作用.Command模式实际上不是个很具体 ...

随机推荐

  1. java实现猴子选大王问题(约瑟夫问题)

    题目:m只猴子围成一圈报数,报n的猴子自动离开,然后下一位重新从1开始报数,一直循环,最后剩下的那个猴子就是猴大王,写出程序求出最后是大王的那只猴子最初的位置. package learn; impo ...

  2. 如何在cmd窗口里快速且正确打开任意位置路径(各版本windows系统都适合)(图文详解)(博主推荐)

    问题的由来 有时候,我们很苦恼,总是先系统键 + R,然后再去手动敲.尤其对win7系统比较麻烦 解决办法 方法一:复制路径(这点对win10系统做得好,直接可以复制) ,win7系统的话可能还需要设 ...

  3. Gradient Optimization

    Gradient Optimization Gradient Descent Batch Gradient Descent Mini-Batch Gradient Descent Stochastic ...

  4. Linux Kernel文件系统写I/O流程代码分析(二)bdi_writeback

    Linux Kernel文件系统写I/O流程代码分析(二)bdi_writeback 上一篇# Linux Kernel文件系统写I/O流程代码分析(一),我们看到Buffered IO,写操作写入到 ...

  5. 点击checkbox 向input 里面传值显示

    文本框显示 <input type="text" id="textareashow" name="" class="marg ...

  6. MyBatis 指定的转换无效

    表字段Pay类型设置的是float,生成类的属性如下: public double Pay{get;set;} 读取列表时出现如下错误: 错误信息: 查看堆栈跟踪信息, get_Decimal()提示 ...

  7. springboot+mybatis实现动态切换数据源

    前几天有个需求,需要使用不同的数据源,例如某业务要用A数据源,另一个业务要用B数据源.我上网收集了一些资料整合了一下,虽然最后这个需求不了了之了,但是多数据源动态切换还是蛮好用的,所以记录一下,或许以 ...

  8. Discuz x3 UCenter实现同步登陆原理

    1.Discuz x3 的登录页面URL是:/member.php?mod=logging&action=login 2.这个登录页面,登录提交的地址是: <form method=&q ...

  9. Linux下mongodb

    Linux下mongodb安装: 新建mongodb文件夹 下载安装包 curl -O https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-3. ...

  10. "UTF-8"、"UTF8"、"utf-8"、"utf8"之间的区别

    本质上没有区别.1.“UTF-8”是标准写法;2.在Windows下边英文不区分大小写,所以也可以写成“utf-8”;3.“UTF-8”也可以把中间的“-”省略,写成“UTF8”.一般程序都能识别,但 ...