设计模式:命令(Command)模式
设计模式:命令(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)模式的更多相关文章
- junit设计模式--命令者模式
命令模式的意图 将一个请求封装成一个对象,从而使你可以用不同的请求对客户进行参数化: 对请求排队或记录请求日志,以及支持可撤销的操作: 命令模式告诉我们可以为一个操作生成一个对象并给出它的一个执行方法 ...
- 设计模式C++描述----19.命令(Command)模式
一. 举例说明 我们知道,在多线程程序中,多个用户都给系统发 Read 和 Write 命令.这里有几点需要说明: 1. 首先明确一点,所有的这些 Read 和 Write 命令都是调用一个库函数. ...
- 命令(Command)模式
命令模式又称为行动(Action)模式或者交易(Transaction)模式. 命令模式把一个请求或者操作封装到一个对象中.命令模式允许系统使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可 ...
- 设计模式 命令-Command
命令-Command 当要向不同类的对象发出相同的请求时,可以将接收者和他的动作封装进一个命令对象.这样调用者只和命令产生依赖.而不会和众多的接收者发生依赖. Head First例子 要设计一款遥控 ...
- python 设计模式之命令(Command)模式
#写在前面 也了解了不少设计模式了,他们都有一个通病,那就是喜欢把简单的东西复杂化.比如在不同的类中加个第三者.哈哈哈,简单变复杂是有目的的,那就是降低耦合度,增强可维护性,提高代码复用性,使代码变得 ...
- 十五、命令(Command)模式--行为型模式(Behavioral Pattern)
命令模式又称为行动(Action)模 式或交易(Transaction)模式.命令模式把一个请求或者操作封装到一个对象中. 命令模式是对命令的封装.命令模式把发出命令的责任和执行命令的责任分割开,委派 ...
- 设计模式:command模式
目的:将命令设计成类的形式,并可以组织成队列 优点: 在需要的情况下,可以比较容易地将命令记入日志 可以容易的实现对请求的撤销和重做 由于新的具体命令类不影响其他的命令类,因此增加新的具体命令类很容易 ...
- 设计模式--命令模式(Command)
基本概念: Command模式也叫命令模式 ,是行为设计模式的一种.Command模式通过被称为Command的类封装了对目标对象的调用行为以及调用参数,命令模式将方法调用给封装起来了. 命令模式的 ...
- 设计模式---行为变化模式之命令模式(Command)
前提:行为变化模式 在组件的构建过程中,组建行为的变化经常导致组件本身剧烈的变化.“行为变化”模式将组件的行为和组件本身进行解耦,从而支持组件的变化,实现两者之间的松耦合. 类中非虚函数和静态函数方法 ...
- Java设计模式(22)命令模式(Command模式)
Command模式是最让我疑惑的一个模式,我在阅读了很多代码后,才感觉隐约掌握其大概原理,我认为理解设计模式最主要是掌握起原理构造,这样才对自己实际编程有指导作用.Command模式实际上不是个很具体 ...
随机推荐
- ambari-server启动出现Caused by: java.lang.RuntimeException:java.lang.ClassNotFoundEception:com.mysql.jdbc.Driver问题解决办法(图文详解)
不多说,直接上干货! 问题详解 启动ambari-server出现 Caused by: java.lang.RuntimeException:java.lang.ClassNotFoundEcept ...
- JS原型与原型链图解
- Magento 2中文手册教程 - 如何获得 Magento 2
Magento 2 安装 我们搜集了一些信息来帮助您开始使用Magento 2和你的Magento 2安装. 我们有一些资源帮助您开始使用Magento 2. 如何获得 Magento 2 参考下表开 ...
- MVVMLight - IOC Containers and MVVM
在面向对象编程的早期,开发者要面对在应用程序或者类库中创建或检索类的实例的问题.针对这个问题有很多的解决方案.在过去几年中,依赖注入(DI)和控制反转(IoC)在开发者中很流行,并且取代了老的方案,比 ...
- Lucene学习之二:Lucene的总体架构
本文转载自:http://www.cnblogs.com/forfuture1978/archive/2009/12/14/1623596.html Lucene总的来说是: 一个高效的,可扩展的,全 ...
- redis(5)持久化
一.持久化 计算机的数据有两种状态: 1)持久态:如硬盘上的数据 2)瞬时态:如内存当中的数据 持久化就是将瞬时数据转换为持久数据的一个过程,但注意持久化并不意味的数据永远存在,针对不同的持久化,数据 ...
- 三、Bean的初始化
一.使用构造器实例化Bean:这是最简单的方式,Spring IOC容器既能使用默认空构造器也能使用有参构造器两种方式创建bean 空构造器 <bean name="bean1&quo ...
- apache和tomcat搭建集群
最近在学习简单的apache服务器和两个tomcat一起搭建集群,这里简单记录一下 1.准备工作 ①搭建一个可以运行的web项目 用maven搭建springmvc项目 ,只要将这里面的web.xml ...
- side Effect
副作用 side Effect 副作用是在计算结果的过程中,系统状态的一种变化,或者与外部世界进行的可观察的交互. 副作用可能包含,但不限于: 1.更改文件系统 2.往数据库里插入数据 3.发送一个h ...
- cocos-creator 脚本逻辑-1
1.节点 编辑组件的脚本文件时.可以通过以下语句获得节点 this 就是当前组件 this.node 拿到组件依附的节点 This.node.parent 拿到组件依附的节点 的父节点 This.no ...