设计模式(二十二)Command模式
一个类在进行工作时会调用自己或者是其他类的方法,虽然调用结果会反映在对象的状态中,但并不会留下工作的历史记录。
这时,如果我们有一个类,用来表示“请进行这项工作”的“命令”就会方便很多。每一项想做的工作就不再是“方法的调用”这种动态处理了,而是一个表示命令的类的实例,即可以用“物”表示。要想管理工作的历史记录,只需管理这些实例的集合即可,而且还可以随时再次执行过去的命令,或是将多个过去的命令整合为一个新命令并执行。
Command有时也被称为时间。当发生点击鼠标、按下键盘按键等事件时,我们可以先将这些事件作成实例,然后按照发生顺序放入队列中。接着,再依次去处理它们。

示例程序要实现的功能是,用户拖动鼠标时程序会绘制出红色圆点,点击clear按钮后会清除所有的圆点。用户每拖动一次鼠标,应用程序都会为“在这个位置画一个点”这条命令生成一个DrawCommand类的实例。只要保存了这条命令,以后有需要时就可以重新绘制。
package bigjunoba.bjtu.command;
public interface Command {
public abstract void execute();
}
Command接口是表示“命令”的接口。作用就是“执行”什么东西。
package bigjunoba.bjtu.command; import java.util.Stack;
import java.util.Iterator; public class MacroCommand implements Command {
// 命令的集合
private Stack<Command> commands = new Stack<Command>();
// 执行
public void execute() {
Iterator<Command> it = commands.iterator();
while (it.hasNext()) {
((Command)it.next()).execute();
}
}
// 添加命令,同时防止不小心将自己(this)添加进去。
public void append(Command cmd) {
if (cmd != this) {
commands.push(cmd);
}
}
// 删除push方法添加的最后一条命令
public void undo() {
if (!commands.empty()) {
commands.pop();
}
}
// 删除所有命令
public void clear() {
commands.clear();
}
}
MacroCommand类表示“由多条命令整合成的命令”。该类实现了Command接口。commands字段是Stack类型的,它是保存了多个Command(即实现了Command接口的实例)的集合。
package bigjunoba.bjtu.drawer; import bigjunoba.bjtu.command.*;
import java.awt.Point; public class DrawCommand implements Command {
// 绘制对象
protected 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);
}
}
DrawCommand类实现了Command接口,表示“绘制一个点的命令”。构造函数的作用是生成“在这个位置绘制点”的命令。execute方法的作用是执行命令。
package bigjunoba.bjtu.drawer;
public interface Drawable {
public abstract void draw(int x, int y);
}
Drawable接口是表示“绘制对象”的接口。draw方法是用于绘制的方法。
package bigjunoba.bjtu.drawer; import bigjunoba.bjtu.command.*;
import java.util.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*; public class DrawCanvas extends Canvas implements Drawable {
// 颜色
private Color color = Color.red;
// 要绘制的圆点的半径
private int radius = 6;
// 命令的历史记录
private MacroCommand history;
// 构造函数
public DrawCanvas(int width, int height, MacroCommand history) {
setSize(width, height);
setBackground(Color.white);
this.history = history;
}
// 重新全部绘制
public void paint(Graphics g) {
history.execute();
}
// 绘制
public void draw(int x, int y) {
Graphics g = getGraphics();
g.setColor(color);
g.fillOval(x - radius, y - radius, radius * 2, radius * 2); //画圆点
}
}
history字段中保存的是DrawCanvas类自己应当执行的绘制命令的集合。该字段是Command.MacroCommand类型的。当需要重新绘制DrawCanvas是,java处理会调用paint方法。它所做的事情仅仅是调用了history.excute方法。这样,记录在history中的所有历史命令都会被重新执行一遍。
package bigjunoba.bjtu.test; import bigjunoba.bjtu.command.*;
import bigjunoba.bjtu.drawer.*; import java.awt.*;
import java.awt.event.*;
import javax.swing.*; public class Main extends JFrame implements ActionListener, MouseMotionListener, WindowListener {
// 绘制的历史记录
private MacroCommand history = new MacroCommand();
// 绘制区域
private DrawCanvas canvas = new DrawCanvas(400, 400, history);
// 删除按钮
private JButton clearButton = new JButton("clear"); // 构造函数
public Main(String title) {
super(title); this.addWindowListener(this);
canvas.addMouseMotionListener(this);
clearButton.addActionListener(this); Box buttonBox = new Box(BoxLayout.X_AXIS);
buttonBox.add(clearButton);
Box mainBox = new Box(BoxLayout.Y_AXIS);
mainBox.add(buttonBox);
mainBox.add(canvas);
getContentPane().add(mainBox); pack();
show();
} // ActionListener接口中的方法
public void actionPerformed(ActionEvent e) {
if (e.getSource() == clearButton) {
history.clear();
canvas.repaint();
}
} // MouseMotionListener接口中的方法
public void mouseMoved(MouseEvent e) {
}
public void mouseDragged(MouseEvent e) {
Command cmd = new DrawCommand(canvas, e.getPoint());
history.append(cmd);
cmd.execute();
}
// WindowListener接口中的方法
public void windowClosing(WindowEvent e) {
System.exit(0);
}
public void windowActivated(WindowEvent e) {}
public void windowClosed(WindowEvent e) {}
public void windowDeactivated(WindowEvent e) {}
public void windowDeiconified(WindowEvent e) {}
public void windowIconified(WindowEvent e) {}
public void windowOpened(WindowEvent e) {} public static void main(String[] args) {
new Main("Command Pattern Sample");
}
}
Main类实现了mouseDragged方法,当鼠标被拖动时,会生成一条“在这个位置画点”的命令,Command cmd = new DrawCommand(canvas, e.getPoint());该命令会先被添加至绘制历史记录中,history.append(cmd);然后立即执行,cmd.execute();。

测试结果如上图所示。

示例程序的时序图。

Command模式的类图。
设计模式(二十二)Command模式的更多相关文章
- Java设计模式(十二) 策略模式
原创文章,同步发自作者个人博客,http://www.jasongj.com/design_pattern/strategy/ 策略模式介绍 策略模式定义 策略模式(Strategy Pattern) ...
- 备忘录模式 Memento 快照模式 标记Token模式 行为型 设计模式(二十二)
备忘录模式 Memento 沿着脚印,走过你来时的路,回到原点. 苦海翻起爱恨 在世间难逃避命运 相亲竟不可接近 或我应该相信是缘份 一首<一生所爱>触动了多少 ...
- 桥接模式 桥梁模式 bridge 结构型 设计模式(十二)
桥接模式Bridge Bridge 意为桥梁,桥接模式的作用就像桥梁一样,用于把两件事物连接起来 意图 将抽象部分与他的实现部分进行分离,使得他们都可以独立的发展. 意图解析 依赖倒置原 ...
- Java 设计模式系列(二十)状态模式
Java 设计模式系列(二十)状态模式 状态模式,又称状态对象模式(Pattern of Objects for States),状态模式是对象的行为模式.状态模式允许一个对象在其内部状态改变的时候改 ...
- JAVA基础知识总结:一到二十二全部总结
>一: 一.软件开发的常识 1.什么是软件? 一系列按照特定顺序组织起来的计算机数据或者指令 常见的软件: 系统软件:Windows\Mac OS \Linux 应用软件:QQ,一系列的播放器( ...
- WCF技术剖析之二十二: 深入剖析WCF底层异常处理框架实现原理[中篇]
原文:WCF技术剖析之二十二: 深入剖析WCF底层异常处理框架实现原理[中篇] 在[上篇]中,我们分别站在消息交换和编程的角度介绍了SOAP Fault和FaultException异常.在服务执行过 ...
- VMware vSphere 服务器虚拟化之二十二桌面虚拟化之创建View Composer链接克隆的虚拟桌面池
VMware vSphere 服务器虚拟化之二十二桌面虚拟化之创建View Composer链接克隆的虚拟桌面池 在上一节我们创建了完整克隆的自动专有桌面池,在创建过程比较缓慢,这次我们将学习创建Vi ...
- Java设计模式(十) 备忘录模式 状态模式
(十九)备忘录模式 备忘录模式目的是保存一个对象的某个状态,在适当的时候恢复这个对象. class Memento{ private String value; public Memento(Stri ...
- Senparc.Weixin.MP SDK 微信公众平台开发教程(二十二):如何安装 Nuget(dll) 后使用项目源代码调试
最近碰到开发者问:我使用 nuget 安装了 Senparc.Weixin SDK,但是有一些已经封装好的过程想要调试,我又不想直接附加源代码项目,这样就没有办法同步更新了,我应该怎么办? 这其实是一 ...
- 学习笔记:CentOS7学习之二十二: 结构化命令case和for、while循环
目录 学习笔记:CentOS7学习之二十二: 结构化命令case和for.while循环 22.1 流程控制语句:case 22.2 循环语句 22.1.2 for-do-done 22.3 whil ...
随机推荐
- chsime.exe cpu占用高
打开管理员的命令提示符,运行 if exist "%SystemRoot%\System32\InputMethod\CHS\ChsIME.exe" (takeown /f &qu ...
- Micronaut 微服务中使用 Kafka
今天,我们将通过Apache Kafkatopic构建一些彼此异步通信的微服务.我们使用Micronaut框架,它为与Kafka集成提供专门的库.让我们简要介绍一下示例系统的架构.我们有四个微型服务: ...
- Spring 梳理-Spring配置文件 -<context:annotation-config/>和<context:component-scan base-package=""/>和<mvc:annotation-driven /> 的区别
<context:annotation-config/> 在基于主机方式配置Spring时,Spring配置文件applicationContext.xml,你可能会见<contex ...
- springboot 集成druid
1.druid简介 Druid首先是一个数据库连接池.Druid是目前最好的数据库连接池,在功能.性能.扩展性方面,都超过其他数据库连接池,包括DBCP.C3P0.BoneCP.Proxool.JBo ...
- 从 axios 源码中了解到的 Promise 链与请求的取消
axios 中一个请求取消的示例: axios 取消请求的示例代码 import React, { useState, useEffect } from "react"; impo ...
- IDEA 学习笔记之 Maven项目开发
Maven项目开发: 配置Maven: 新建Maven项目: 选择webapp: 和eclipse一样,设置: 修改maven配置,添加一个新属性,可以加快项目创建速度: 完成: 新建java和tes ...
- 使用echarts画一个类似组织结构图的图表
昨天,写了一篇关于圆环进度条的博客(请移步:Vue/React圆环进度条),已经烦不胜烦,今天又遇到了需要展示类似公司的组织结构图的功能需求,要冒了!!! 这种需求,自己用div+css也是可以实现的 ...
- 【CPU】解决打开360或者Chrome浏览器CPU占用过高
cmd 运行: RD /s /q "%USERPROFILE%\AppData\Roaming\Microsoft\Protect"
- 什么是线程调度器(Thread Scheduler)和时间分片(Time Slicing )?
线程调度器是一个操作系统服务,它负责为 Runnable 状态的线程分配 CPU 时间. 一旦我们创建一个线程并启动它,它的执行便依赖于线程调度器的实现.同上一个问题,线程调度并不受到 Java 虚拟 ...
- (4)一起来看下mybatis框架的缓存原理吧
本文是作者原创,版权归作者所有.若要转载,请注明出处.本文只贴我觉得比较重要的源码,其他不重要非关键的就不贴了 我们知道.使用缓存可以更快的获取数据,避免频繁直接查询数据库,节省资源. MyBatis ...