设计模式:命令(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 PL/SQL编程之函数

    注: 以下测试案例所用的表均来自与scott方案,使用前,请确保该用户解锁. 代码的执行环境是在sqlplus中 1.简介 函数用于返回特定的数据,当建立函数时,函数头部必须包含return子句,而在 ...

  2. 关于new Option()

    先来了解下,如何运用js实现select动态添加option. //1.动态创建select function createSelect(){ var mySelect = document.crea ...

  3. Ubuntu安装Python的mysqlclient

    介绍 本人想在Ubuntu上开发Python程序,使用MySQL数据库. 安装环境: Ubuntu14.04 安装MySQL数据库 具体步骤如下: apt-get update apt-get ins ...

  4. linux 查看端口,开启新端口

    一.查看端口被占用命令 1.lsof -i:端口号 2.netstat -tunlp|grep 端口号 3.netstat -anp 查看哪些端口被打开 上面命令是查看端口被进程占用的情况 二.开启新 ...

  5. Ubuntu apache

    Linux系统为Ubuntu 1. 启动apache服务 # /etc/init.d/apache2 start 2. 重启apache服务 # /etc/init.d/apache2 restart ...

  6. 级联sql

    select ID, PID, NAME,KEY from HS_DICT start with KEY = 'HS_EXP_WORK_LOCATION'connect by prior ID = P ...

  7. 如何在不接入微信API的情况下自定义分享内容(图片、链接、标题)

    方法如下: 1.设置分享title:动态改变document.title值即可: document.title = 'test' 2.设置分享图片:在页面隐藏一张尺寸大于290*290的图(图片需要容 ...

  8. 问题集录06--SpringBoot创建Maven项目

    1. 如下图,打开idea之后,file -> new -> project2. 如下图,在弹出的new project 页面,选择maven -> 勾选Create from ar ...

  9. 获取URL中某个参数的值

    JS代码: function getQueryString(name){ var reg = new RegExp("(^|&)" + name + "=([^& ...

  10. show_space

    create or replace procedure show_space( p_segname_1 in varchar2,p_space in varchar2 default 'AUTO',p ...