设计模式:命令(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. Oracle Net Configuration(监听程序和网络服务配置)

    1.在Oracle服务端和客户端都安装完之后,就需要配置监听程序和本地网络服务,以便外部程序和工具的访问,所以Oracle提供了两款自带的工具来配置它们分别是 Net Configuration.Ne ...

  2. 聊聊Python ctypes 模块(转载)

    https://zhuanlan.zhihu.com/p/20152309?columnSlug=python-dev 作者:Jerry Jho链接:https://zhuanlan.zhihu.co ...

  3. Robot Framework_Ride(Settings)

    Settings 不管是测试套件还是测试用例都会有一个“Settings>>”的按钮,因为它默认是被折叠起来的,所以,一般不太容易发现它,更不知道点击它之后是可以展开的 1.测试用例的 S ...

  4. 分布式集群HBase启动后某节点的HRegionServer自动消失问题

    详细问题   我这里是,我的这个slave1的HRegionServer 进程启动后,不久自动消失.           去查看日志,排查问题: 发现问题: 解决办法 [hadoop@master h ...

  5. Serverless+SCF=打倒服务器,解放程序员

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由云加社区技术沙龙 发表于云+社区专栏 "你做什么工作的?" "程序员." "那正好, ...

  6. mysql 8 root密码重置

    亲测有效. https://blog.csdn.net/gupao123456/article/details/80766154

  7. solr 索引库的维护

    一.配置中文分析器:IK-analyzer,在FieldType中指定中文分析器:1 复制IK-analyzer到你的服务器指定目录中.2 在该目录中,我们需要的东西有:IKAnalyzer的jar包 ...

  8. heroku快速部署node应用

    试了一下heroku,简直碉堡了,下面介绍如何简单几步实现弄得应用的部署访问: 1.首先https://dashboard.heroku.com/进行账号注册 2.github上push一个最新的no ...

  9. Node.js学习笔记(四) --- fs模块的使用

    目录 . fs.stat 检测是文件还是目录 . fs.mkdir 创建目录 . fs.writeFile 创建写入文件 . fs.appendFile 追加文件 . fs.readFile 读取文件 ...

  10. iframe适应屏幕大小,隐藏滚动条,移动端

    <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head runat=&qu ...