设计模式14---设计模式之命令模式(Command)(行为型)
1.场景模拟
请用软件模拟开机过程
按下启动按钮
然后电源供电
主板开始加电自检
BIOS依次寻找其他设备的BIOS并且让他们初始化自检
开始检测CPU,内存,光盘,硬盘,光驱,串口,并口,软驱即插即用设备
进入系统引导
上面的过程可以抽象为如下:
客户端只是想要发出命令或者请求,不关心请求的真正接受者是谁,也不关心具体如何实现,而且同一个请求的动作可以有不同的请求内容,当然具体的处理功能也不一样。请问如何实现呢?
2.使用命令模式来解决问题
2.1命令模式定义
将一个请求封装为一个对象,从而是你可以用不同的请求对客户进行参数化,队请求排队或者请求日志,以及支持可撤销的操作。
2.2命令模式的结构图
在命令模式中,会定义一个命令的接口,用来约束所有的命令对象,然后提供具体的命令实现,每个命令实现对象是对客户端某个请求的封装,对应于机箱上的按钮,一个机箱上可以有很多按钮,也就相当于会有多个具体的命令实现对象。在命令模式中,命令对象并不知道如何处理命令,会有相应的接受者对象来真正执行命令。
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代码示例结构图
机箱上的按钮就相当于命令对象
机箱相当于调用者
主板相当于接受者对象
命令对象持有一个接受者对象,相当于给机箱的按钮连了一根连接线
当机箱上的按钮被按下的时候,机箱就把这个命令通过连接线发送出去。
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命令模式的调用顺序图如下
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)(行为型)的更多相关文章
- 命令模式 Command 行为型 设计模式(十八)
命令模式(Command) 请分析上图中这条命令的涉及到的角色以及执行过程,一种可能的理解方式是这样子的: 涉及角色为:大狗子和大狗子他妈 过程为:大狗子他妈角色 调用 大狗子的“回家吃饭”方法 引子 ...
- 【设计模式 - 14】之命令模式(Command)
1 模式简介 命令模式的定义: 命令模式将命令封装成对象,从而使调用一个命令变为调用一个对象的指定方法. 命令模式的优点: 1) 降低了系统耦合度: 2) 新的命 ...
- Headfirst设计模式的C++实现——命令模式(Command)
先看如果不用命令模式的实现: light.h #ifndef _LIGHT_H_ #define _LIGHT_H #include <iostream> class LIGHT { pu ...
- 设计模式14:Command 命令模式(行为型模式)
Command 命令模式(行为型模式) 耦合与变化 耦合是软件不能抵御变化的根本性原因.不仅实体对象与实体对象之间存在耦合关系,实体对象与行为操作之间也存在耦合关系. 动机(Motivation) 在 ...
- Java设计模式(22)命令模式(Command模式)
Command模式是最让我疑惑的一个模式,我在阅读了很多代码后,才感觉隐约掌握其大概原理,我认为理解设计模式最主要是掌握起原理构造,这样才对自己实际编程有指导作用.Command模式实际上不是个很具体 ...
- 设计模式 ( 十三 ) 命令模式Command(对象行为型)
设计模式 ( 十三 ) 命令模式Command(对象行为型) 1.概述 在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需 ...
- 乐在其中设计模式(C#) - 命令模式(Command Pattern)
原文:乐在其中设计模式(C#) - 命令模式(Command Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 命令模式(Command Pattern) 作者:webabcd ...
- Java 设计模式系列(十四)命令模式(Command)
Java 设计模式系列(十四)命令模式(Command) 命令模式把一个请求或者操作封装到一个对象中.命令模式允许系统使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复 ...
- 设计模式 - 命令模式(command pattern) 多命令 具体解释
命令模式(command pattern) 多命令 具体解释 本文地址: http://blog.csdn.net/caroline_wendy 參考命令模式: http://blog.csdn.ne ...
- 设计模式 - 命令模式(command pattern) 具体解释
命令模式(command pattern) 详细解释 本文地址: http://blog.csdn.net/caroline_wendy 命令模式(command pattern) : 将请求封装成对 ...
随机推荐
- ARGB和RGB
ARGB 一种色彩模式,也就是RGB色彩模式附加上Alpha(透明度)通道,常见于32位位图的存储结构. ARGB---Alpha,Red,Green,Blue. Alpha-图像通道 如果图形卡具有 ...
- Memcached 搭建过程
原文链接:http://www.open-open.com/lib/view/open1324368235733.html 安装 memcached 服务端 yum -y install libeve ...
- JavaAppArguments.java程序的更改
此程序模仿JvaAppArgyments.java编写,从命令行接受多个数字,求和之后输出结果. 设计思想:命令行参数都是字符串,可以考虑用 Integer.parseInt(args[]) ...
- 狗狗40题~(Volume A)
A - The Willy Memorial Program 大模拟题…… 一开始的思路不对,修修补补WA了十发.当时想直接一个并查集做连通来搞定它,结果发现不能很好地判断各管的水位.究其原因还是因为 ...
- Postman 安装及使用入门教程(转)
安装 本文只是基于 Chrome 浏览器的扩展插件来进行的安装,并非单独应用程序. 首先,你要台电脑,其次,安装有 Chrome 浏览器,那你接着往下看吧. 1. 官网安装(别看) 打开官网,http ...
- 机器学习之python: kNN
################################################## # kNN : k Nearest Neighbour # Author : Monne # Da ...
- Html中input标签的使用
1.取消按钮按下时的虚线框 在input里添加属性值 hideFocus 或者 HideFocus=true 2.只读文本框内容 在input里添加属性值 readonly 3.防止退后清空的TEXT ...
- 转:sprintf与snprintf
sprintf与snprintf int sprintf( char *buffer, const char *format [, argument] ... ); 除了前两个参数类型固定外,后面 ...
- The 500 Most Commonly Used Words in the English Language
Based on the combined results of British English, American English and Australian English surveys of ...
- 手机上使用asmack开发xmpp客户端
openfire服务端,smack: 下载地址:http://www.igniterealtime.org/downloads/index.jsp 源代码:http://www.ign ...