设计模式14---设计模式之命令模式(Command)(行为型)

摘要:
在命令模式中,命令对象并不知道如何处理命令,会有相应的接受者对象来真正执行命令。

1.场景模拟

请用软件模拟开机过程
按下启动按钮
然后电源供电
主板开始加电自检
BIOS依次寻找其他设备的BIOS并且让他们初始化自检
开始检测CPU,内存,光盘,硬盘,光驱,串口,并口,软驱即插即用设备
进入系统引导
上面的过程可以抽象为如下:
客户端只是想要发出命令或者请求,不关心请求的真正接受者是谁,也不关心具体如何实现,而且同一个请求的动作可以有不同的请求内容,当然具体的处理功能也不一样。请问如何实现呢?

2.使用命令模式来解决问题

2.1命令模式定义

将一个请求封装为一个对象,从而是你可以用不同的请求对客户进行参数化,队请求排队或者请求日志,以及支持可撤销的操作。

2.2命令模式的结构图

在命令模式中,会定义一个命令的接口,用来约束所有的命令对象,然后提供具体的命令实现,每个命令实现对象是对客户端某个请求的封装,对应于机箱上的按钮,一个机箱上可以有很多按钮,也就相当于会有多个具体的命令实现对象。在命令模式中,命令对象并不知道如何处理命令,会有相应的接受者对象来真正执行命令。

设计模式14---设计模式之命令模式(Command)(行为型)第1张

3.命令模式示例代码详解

3.1命令接口,声明执行的操作

package demo12.command.example1;
/**
 * 命令接口,声明执行的操作
 */
public interface Command {
	/**
	 * 执行命令对应的操作
	 */
	public void execute();
}

3.2具体的命令实现对象

package demo12.command.example1;
/**
 * 具体的命令实现对象
 */
public class ConcreteCommand implements Command {
	/**
	 * 持有相应的接受者对象
	 */
	private Receiver receiver = null;
	/**
	 * 示意,命令对象可以有自己的状态
	 */
	private String state;
	/**
	 * 构造方法,传入相应的接受者对象
	 * @param receiver 相应的接受者对象
	 */
	public ConcreteCommand(Receiver receiver){
		this.receiver = receiver;
	}
	public void execute() {
		//通常会转调接受者对象的相应方法,让接受者来真正执行功能
		receiver.action();
	}
}

3.3接收者对象

package demo12.command.example1;
/**
 * 接收者对象
 */
public class Receiver {
	/**
	 * 示意方法,真正执行命令相应的操作
	 */
	public void action(){
		//真正执行命令操作的功能代码
	}
}

3.4调用者对象

package demo12.command.example1;
/**
 * 调用者
 */
public class Invoker {
	/**
	 * 持有命令对象
	 */
	private Command command = null;
	/**
	 * 设置调用者持有的命令对象
	 * @param command 命令对象
	 */
	public void setCommand(Command command) {
		this.command = command;
	}
	/**
	 * 示意方法,要求命令执行请求
	 */
	public void runCommand() {
		//调用命令对象的执行方法
		command.execute();
	}
}

3.5客户端使用

package demo12.command.example1;
public class Client {
	/**
	 * 示意,负责创建命令对象,并设定它的接受者
	 */
	public void assemble() {
		// 创建接受者
		Receiver receiver = new Receiver();
		// 创建命令对象,设定它的接收者
		Command command = new ConcreteCommand(receiver);
		// 创建Invoker,把命令对象设置进去
		Invoker invoker = new Invoker();
		invoker.setCommand(command);
	}
}

4.使用命令模式来实现场景中的问题

4.1代码示例结构图

机箱上的按钮就相当于命令对象
机箱相当于调用者
主板相当于接受者对象
命令对象持有一个接受者对象,相当于给机箱的按钮连了一根连接线
当机箱上的按钮被按下的时候,机箱就把这个命令通过连接线发送出去。
设计模式14---设计模式之命令模式(Command)(行为型)第2张

4.2定义主板接口

package demo12.command.example2;
/**
 * 主板的接口
 */
public interface MainBoardApi {
	/**
	 * 主板具有能开机的功能
	 */
	public void open();
}

4.3技嘉主板

package demo12.command.example2;
/**
 * 技嘉主板类,开机命令的真正实现者,在Command模式中充当Receiver
 */
public class GigaMainBoard implements MainBoardApi{
	/**
	 * 真正的开机命令的实现
	 */
	public void open(){
		System.out.println("技嘉主板现在正在开机,请等候");
		System.out.println("接通电源......");
		System.out.println("设备检查......");
		System.out.println("装载系统......");
		System.out.println("机器正常运转起来......");
		System.out.println("机器已经正常打开,请操作");
	}
}

4.4命令接口

package demo12.command.example2;
/**
 * 命令接口,声明执行的操作
 */
public interface Command {
	/**
	 * 执行命令对应的操作
	 */
	public void execute();
}

4.5命令的具体实现

package demo12.command.example2;
/**
 * 开机命令的实现,实现Command接口,
 * 持有开机命令的真正实现,通过调用接收者的方法来实现命令
 */
public class OpenCommand implements Command{
	/**
	 * 持有真正实现命令的接收者——主板对象
	 */
	private MainBoardApi mainBoard = null;
	/**
	 * 构造方法,传入主板对象
	 * @param mainBoard 主板对象
	 */
	public OpenCommand(MainBoardApi mainBoard) {
		this.mainBoard = mainBoard;
	}
	public void execute() {
		//对于命令对象,根本不知道如何开机,会转调主板对象
		//让主板去完成开机的功能
		this.mainBoard.open();
	}
}

4.6提供机箱

package demo12.command.example2;
/**
 * 机箱对象,本身有按钮,持有按钮对应的命令对象
 */
public class Box {
	/**
	 * 开机命令对象
	 */
	private Command openCommand;
	/**
	 * 设置开机命令对象
	 * @param command 开机命令对象
	 */
	public void setOpenCommand(Command command){
		this.openCommand = command;
	}
	/**
	 * 提供给客户使用,接受并相应用户请求,相当于按钮被按下触发的方法
	 */
	public void openButtonPressed(){
		//按下按钮,执行命令
		openCommand.execute();
	}
}

4.7客户端使用

package demo12.command.example2;
public class Client {
	public static void main(String[] args) {
		//1:把命令和真正的实现组合起来,相当于在组装机器,
		//把机箱上按钮的连接线插接到主板上。
		MainBoardApi mainBoard = new GigaMainBoard();
		OpenCommand openCommand = new OpenCommand(mainBoard);
		//2:为机箱上的按钮设置对应的命令,让按钮知道该干什么
		Box box = new Box();
		box.setOpenCommand(openCommand);
		//3:然后模拟按下机箱上的按钮
		box.openButtonPressed();
	}
}

5.命令模式讲解

5.1命令模式的关键

就是把请求也封装为对象
命令模式中通常会有一个命令的组装者,用它来维护虚实现和真实实现之间的关系。
并且发起请求的对象和真正实现的对象是解耦的。

5.2命令模式的调用顺序图如下

设计模式14---设计模式之命令模式(Command)(行为型)第3张

6.命令的参数化配置

可以用不同的命令对象,去参数化配置客户的请求。
实际上也很简单,举个例子:比如添加一个重启功能。
那么就在MainBoardApi上添加一个reset()函数即可,然后再重新写一个ResetCommand实现Command,当然box中也要添加一个重启的函数resetButtonPressed()就行啦。那么客户端调用就如下

package demo12.command.example3;
public class Client {
	public static void main(String[] args) {
		//1:把命令和真正的实现组合起来,相当于在组装机器,
		//把机箱上按钮的连接线插接到主板上。
		MainBoardApi mainBoard = new GigaMainBoard();
		//创建开机命令
		OpenCommand openCommand = new OpenCommand(mainBoard);
		//创建重启机器的命令
		ResetCommand resetCommand = new ResetCommand(mainBoard);
		//2:为机箱上的按钮设置对应的命令,让按钮知道该干什么
		Box box = new Box();
		//先正确配置,就是开机按钮对开机命令,重启按钮对重启命令
		box.setOpenCommand(openCommand);
		box.setResetCommand(resetCommand);
		//3:然后模拟按下机箱上的按钮
		System.out.println("正确配置下------------------------->");
		System.out.println(">>>按下开机按钮:>>>");
		box.openButtonPressed();
		System.out.println(">>>按下重启按钮:>>>");
		box.resetButtonPressed();
		//然后来错误配置一回,反正是进行参数化配置
		//就是开机按钮对重启命令,重启按钮对开机命令
		box.setOpenCommand(resetCommand);
		box.setResetCommand(openCommand);
		//4:然后还是来模拟按下机箱上的按钮
		System.out.println("错误配置下------------------------->");
		System.out.println(">>>按下开机按钮:>>>");
		box.openButtonPressed();
		System.out.println(">>>按下重启按钮:>>>");
		box.resetButtonPressed();
	}
}

7.可撤销的操作(重点)

可撤销操作的意思就是:放弃该操作,回到未执行该操作的状态。
有两种基本思路:
1.补尝试,又称反操作式。
打开关闭 加减
2.存储恢复式
把操作前得状态记录下来。

8.补尝试,又称反操作式(以计算器为例)

8.1操作运算接口(相当于主板接口)

package demo12.command.example4;
/**
 * 操作运算的接口
 */
public interface OperationApi {
	/**
	 * 获取计算完成后的结果
	 * @return 计算完成后的结果
	 */
	public int getResult();
	/**
	 * 设置计算开始的初始值
	 * @param result 计算开始的初始值
	 */
	public void setResult(int result);
	/**
	 * 执行加法
	 * @param num 需要加的数
	 */
	public void add(int num);
	/**
	 * 执行减法
	 * @param num 需要减的数
	 */
	public void substract(int num);
}

8.2操作运算真实实现类(相当于主板)

package demo12.command.example4;
/**
 * 运算类,真正实现加减法运算
 */
public class Operation implements OperationApi{
	/**
	 * 记录运算的结果
	 */
	private int result;
	public int getResult() {
		return result;
	}
	public void setResult(int result) {
		this.result = result;
	}
	public void add(int num){
		//实现加法功能
		result += num;
	}
	public void substract(int num){
		//实现减法功能
		result -= num;
	}
}

8.3抽象命令接口

package demo12.command.example4;
/**
 * 命令接口,声明执行的操作,支持可撤销操作
 */
public interface Command {
	/**
	 * 执行命令对应的操作
	 */
	public void execute();
	/**
	 * 执行撤销命令对应的操作
	 */
	public void undo();
}

8.4真实实现命令类(加命令和减命令)

package demo12.command.example4;
/**
 * 具体的加法命令实现对象
 */
public class AddCommand implements Command{
	/**
	 * 持有具体执行计算的对象
	 */
	private OperationApi operation = null;
	/**
	 * 操作的数据,也就是要加上的数据
	 */
	private int opeNum;
	/**
	 * 构造方法,传入具体执行计算的对象
	 * @param operation 具体执行计算的对象
	 * @param opeNum 要加上的数据
	 */
	public AddCommand(OperationApi operation,int opeNum){
		this.operation = operation;
		this.opeNum = opeNum;
	}
	public void execute() {
		//转调接收者去真正执行功能,这个命令是做加法
		this.operation.add(opeNum);
	}
	public void undo() {
		//转调接收者去真正执行功能
		//命令本身是做加法,那么撤销的时候就是做减法了
		this.operation.substract(opeNum);
	}
}
package demo12.command.example4;
/**
 * 具体的减法命令实现对象
 */
public class SubstractCommand implements Command{
	/**
	 * 持有具体执行计算的对象
	 */
	private OperationApi operation = null;
	/**
	 * 操作的数据,也就是要减去的数据
	 */
	private int opeNum;
	/**
	 * 构造方法,传入具体执行计算的对象
	 * @param operation 具体执行计算的对象
	 * @param opeNum 要减去的数据
	 */
	public SubstractCommand(OperationApi operation,int opeNum){
		this.operation = operation;
		this.opeNum = opeNum;
	}	
	public void execute() {
		//转调接收者去真正执行功能,这个命令是做减法
		this.operation.substract(opeNum);
	}
	public void undo() {
		//转调接收者去真正执行功能
		//命令本身是做减法,那么撤销的时候就是做加法了
		this.operation.add(opeNum);
	}
}

8.5计算器类(相当于机箱)

package demo12.command.example4;
import java.util.*;
/**
 * 计算器类,计算器上有加法按钮、减法按钮,还有撤销和恢复的按钮
 */
public class Calculator {
	/**
	 * 命令的操作的历史记录,在撤销时候用
	 */
	private List<Command> undoCmds = new ArrayList<Command>();
	/**
	 * 命令被撤销的历史记录,在恢复时候用
	 */
	private List<Command> redoCmds = new ArrayList<Command>();
	private Command addCmd = null;
	private Command substractCmd = null;
	public void setAddCmd(Command addCmd) {
		this.addCmd = addCmd;
	}
	public void setSubstractCmd(Command substractCmd) {
		this.substractCmd = substractCmd;
	}	
	public void addPressed(){
		this.addCmd.execute();
		//把操作记录到历史记录里面
		undoCmds.add(this.addCmd);
	}
	public void substractPressed(){
		this.substractCmd.execute();
		//把操作记录到历史记录里面
		undoCmds.add(this.substractCmd);
	}
	public void undoPressed(){
		if(this.undoCmds.size()>0){
			//取出最后一个命令来撤销
			Command cmd = this.undoCmds.get(this.undoCmds.size()-1);
			cmd.undo();
			//如果还有恢复的功能,那就把这个命令记录到恢复的历史记录里面
			this.redoCmds.add(cmd );
			//然后把最后一个命令删除掉,
			this.undoCmds.remove(cmd);
		}else{
			System.out.println("很抱歉,没有可撤销的命令");
		}
	}
	public void redoPressed(){
		if(this.redoCmds.size()>0){
			//取出最后一个命令来重做
			Command cmd = this.redoCmds.get(this.redoCmds.size()-1);
			cmd.execute();		
			//把这个命令记录到可撤销的历史记录里面
			this.undoCmds.add(cmd);
			//然后把最后一个命令删除掉
			this.redoCmds.remove(cmd);
		}else{
			System.out.println("很抱歉,没有可恢复的命令");
		}
	}
}

8.6客户端使用

package demo12.command.example4;
public class Client {
	public static void main(String[] args) {
		//1:组装命令和接收者
		//创建接收者
		OperationApi operation = new Operation();
		//创建命令对象,并组装命令和接收者
		AddCommand addCmd = new AddCommand(operation,5);
		SubstractCommand substractCmd = new SubstractCommand(operation,3);
		//2:把命令设置到持有者,就是计算器里面
		Calculator calculator = new Calculator();
		calculator.setAddCmd(addCmd);
		calculator.setSubstractCmd(substractCmd);
		//3:模拟按下按钮,测试一下
		calculator.addPressed();
		System.out.println("一次加法运算后的结果为:"+operation.getResult());
		calculator.substractPressed();
		System.out.println("一次减法运算后的结果为:"+operation.getResult());
		//测试撤消
		calculator.undoPressed();
		System.out.println("撤销一次后的结果为:"+operation.getResult());
		calculator.undoPressed();
		System.out.println("再撤销一次后的结果为:"+operation.getResult());
		//测试恢复
		calculator.redoPressed();
		System.out.println("恢复操作一次后的结果为:"+operation.getResult());
		calculator.redoPressed();
		System.out.println("再恢复操作一次后的结果为:"+operation.getResult());
	}
}

9.存储恢复式

先说几个概念:
宏命令:多个命令的集合的命令
队列请求:就是对命令对象进行排队,组成工作队列,然后依次取出命令对象来执行。
存储恢复式的要点就在于将请求队列日志化,具体的案例我没有理解的很清楚, 以后遇到合适的例子再重新补充。

10.思考命令模式

10.1命令模式本质:

封装请求

10.2什么时候选用

需要抽象出需要执行的动作,并参数化这些对象
需要在不同时刻指定排列和执行请求
需要支持取消操作
当支持系统崩溃时,能将系统的操作功能重新执行一遍

免责声明:文章转载自《设计模式14---设计模式之命令模式(Command)(行为型)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇mysql中时间比较的实现关于 Delphi 中流的使用(1) 用 TMemoryStream(内存流) 入门下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

CentOS 下Mysql数据库的安装与配置

一、mysql简介 说到数据库,我们大多想到的是关系型数据库,比如mysql、oracle、sqlserver等等,这些数据库软件在windows上安装都非常 的方便,在Linux上如果要安装数据库,咱不得不首先推荐的是mysql数据库了,而且Mysql数据库的第一个版本就是发行在Linux系统上的。 MySQL是一个关系型数据库管理系统,由瑞典MySQL...

mac 终端 常用命令

http://blog.csdn.net/y2888886/article/details/50468198 mac 终端 常用命令 基本命令1、列出文件ls 参数 目录名        例: 看看驱动目录下有什么:ls /System/Library/Extensions参数 -w 显示中文,-l 详细信息, -a 包括隐藏文件2、转换目录   ...

97 条 Linux 常用命令及Vim命令总结

一:Vim编辑模式命令 基本上Vim共分为3种模式,分别是一般模式,编辑模式和命令行模式,这三种模式的作用分别如下简述: 一般模式:默认模式。打开vim直接进入的是一般模式,在这个模式下,可以进行的操作有:移动光标,复制,粘贴,删除。 编辑模式:编辑文件内容,在界面左下方会出现INSERT的字样。 命令行模式:查找、读取、保存、替换字符、显示行号、离开v...

inux反选删除文件

最简单的方法是 # shopt -s extglob      (打开extglob模式) # rm -fr !(file1)  如果是多个要排除的,可以这样: # rm -rf !(file1|file2)  Linuxrm删除指定文件外的其他文件方法汇总 一、Linux下删除文件和文件夹常用命令如下: 删除文件: rm file删除文件夹: rm ...

sed的使用

1 SED 手册 中央研究院计算中心ASPAC 计划(刘刚2008年5月7号整理ganghust@gmail.com) aspac@phi.sinica.edu.tw 技术报告: 96005 1996年12月1日Version:1.0 SED 手册..........................................................

Ant命令总结

1 Ant是什么? Apache Ant 是一个基于 Java的生成工具。生成工具在软件开发中用来将源代码和其他输入文件转换为可执行文件的形式(也有可能转换为可安装的产品映像形式)。随着应用程序的生成过程变得更加复杂,确保在每次生成期间都使用精确相同的生成步骤,同时实现尽可能多的自动化,以便及时产生一致的生成版本2 下载、安装Ant 安装Ant下载.zip...