设计模式:命令(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. db2 tsm backup fails with rc–50(1)

    2015-01-05-19.21.54.477532+000 E8484227A347       LEVEL: Error PID     : 10027058             TID  : ...

  2. (转)搭建企业内部yum仓库(centos6+centos7+epel源)

    搭建企业内部yum仓库(centos6+centos7+epel源) 原文:https://www.cnblogs.com/nulige/p/6081192.html https://www.linu ...

  3. selenium+python(数据驱动测试)

    自动化领域的两种驱动,对象驱动与数据驱动 数据驱动:测试数据的改变引起执行结果的改变 叫 数据驱动 关键字驱动:测试对象名字的改变起引起测试结果的改变 叫 关键字驱动 1 .读取文件参数化   以百度 ...

  4. 解析ES6 Promise

    ES6 Promise 概念之类的,大概读者都应该有所知道,接下来我们直入终点. 先让我们来看看什么是Promise吧,他是一个object,类,arry,function? 首先,学习它的时候应该讲 ...

  5. java.utils.UUID类介绍

    1 UUID介绍 UUID (Universally Unique Identifier)缩写,即通用唯一识别码,也是被开源软件基金会 (Open Software Foundation, OSF) ...

  6. WPF的布局--StackPanel

    1. StackPanel是以堆叠的方式来显示控件(从左到右,或者从上到下) 默认是从上到下显示的,并且宽度为StackPanel的宽度,高度自动适应控件中内容的高度(未对控件进行设置时) 如图: 代 ...

  7. Codeforces 671 A——Recycling Bottles——————【思维题】

     Recycling Bottles time limit per test 2 seconds memory limit per test 256 megabytes input standard ...

  8. 深入理解JavaScript系列(45):代码复用模式(避免篇)

    介绍 任何编程都提出代码复用,否则话每次开发一个新程序或者写一个新功能都要全新编写的话,那就歇菜了,但是代码复用也是有好要坏,接下来的两篇文章我们将针对代码复用来进行讨论,第一篇文避免篇,指的是要尽量 ...

  9. (C++学习)关于CString的一些疑问

    #include <iostream> #include <string> #include <afx.h> #include <vector> usi ...

  10. js密码强度校验

    function AuthPasswd(string) { if(!string){ jQuery("#low").removeClass("org"); }) ...