设计模式之命令模式(Command Pattern)
一.什么是命令模式?
命令模式,封装了方法调用细节,以解耦请求者与执行者,具体流程如下:
1.从请求者(客户)的角度看
请求者(客户)发出请求 -> 调用者(系统)构造命令对象封装请求 -> 调用者调用命令对象的指定方法(请求被执行)
很明显,请求者根本不知道执行者是谁,更不知道具体执行细节。当然请求者本身也并不关心这些,它只要知道请求被执行了就好。
2.从执行者(低层组件)的角度看
执行者(低层组件)被调用 -> 执行者调用内部方法(请求被执行)
同样的,执行者根本不知道请求者是谁,甚至不清楚调用者,不过没关系,执行者只要本本分分的做好本职工作就好了,没必要知道领导的情况。
3.从调用者(系统)的角度看
接到请求 -> 创建命令对象封装请求 -> 在适当的时候调用命令对象的动作来执行请求(请求被执行)
调用者不知道执行者是谁,也不清楚请求者,它只负责构造命令并控制命令被执行,这就足够了。
从上面可以看出各个对象之间的低耦合关系:
请求者(客户)与执行者(低层组件)被彻底解耦,作为中间人的调用者也不了解请求者与执行者的具体细节,它们被很好的保护了起来
这正是我们想要的。
二.举个例子
现实世界中任何一个稍微复杂的子系统都应当有一套命令,比如餐馆的运行机制:
顾客A来到餐馆点一碗面(发出请求) -> 柜台服务员记录下来(创建命令) -> 服务员把小票扔给厨房 -> 厨师C很快做好了一碗面(请求被执行)
顾客不知道将由谁来做这碗面,柜台服务员也不知道,厨师不知道是谁点了这碗面,只知道做完面就可以休息了
是不是与命令模式很相像?
不妨用代码来实现上面的机制
首先,我们需要一个命令接口,毕竟命令才是命令模式的核心,没有命令,一切都是空想
package CommandPattern; /**
* @author ayqy
* 定义Command接口
*/
public interface Command {
public abstract void execute();//只需要定义一个统一的执行方法
}
有了命令还需要执行者,否则只有将军没有小兵,餐馆的执行者当然是厨师:
package CommandPattern; /**
* @author ayqy
* 定义Chef基类
*/
public abstract class Chef {
//在此定义厨师的公共属性 /**
* 定义烹饪方法
*/
public abstract void cook();
//在此定义其它有用的方法
}
我们还需要实现具体的厨师,术业有专攻:
做面的厨师:
package CommandPattern; /**
* @author ayqy
* 定义专业做面的厨师
*/
public class NoodlesChef extends Chef{ @Override
public void cook() {
System.out.println("做好了一碗美味的拉面");
}
}
做饼的厨师:
package CommandPattern; /**
* @author ayqy
* 定义专业做饼的厨师
*/
public class PieChef extends Chef{ @Override
public void cook() {
System.out.println("做好了一块香喷喷的大饼");
}
}
有了小兵,有了将军,我们还需要一套完整的命令:
package CommandPattern; /**
* @author ayqy
* 实现具体NoodlesCommand
*/
public class NoodlesCommand implements Command{
private NoodlesChef chef;//专业做面的厨师 public NoodlesCommand(){
chef = new NoodlesChef();
} @Override
public void execute() {
chef.cook();
//调用其它需要的方法
}
}
package CommandPattern; /**
* @author ayqy
* 实现具体PieCommand
*/
public class PieCommand implements Command{
private PieChef chef;//专业做饼的厨师 public PieCommand(){
chef = new PieChef();
} @Override
public void execute() {
chef.cook();
//调用其它需要的方法
}
}
准备工作做好了,餐馆可以开张了
三.效果示例
需要一个Test类:
package CommandPattern; /**
* @author ayqy
* 实现测试类
*/
public class Test { public static void main(String[] args) {
System.out.println("Command Pattern餐馆开张。。");
System.out.println("第一位客户X先生");
System.out.println("X先生:你好,我需要一碗面,我饿极了");
NoodlesCommand nCmd = new NoodlesCommand();
System.out.println("柜台服务员:好的,我已经记下了,马上就好");
System.out.println("柜台服务员:厨房~~,接单");
nCmd.execute();
System.out.println("X先生:真快啊!"); System.out.println(); System.out.println("第二位客户XX先生");
System.out.println("XX先生:你好,我需要一块饼,20分钟后来取");
PieCommand pCmd = new PieCommand();
System.out.println("柜台服务员:好的,我已经记下了");
System.out.println("15分钟后");
System.out.println("柜台服务员:厨房~~,接单");
pCmd.execute();
System.out.println("XX先生:真准时啊!");
}
}
结果示例:
从例子可以看出:
1.调用者(柜台服务员)可以控制具体执行时机,但对具体执行者(厨师)的细节完全不清楚
2.请求者(顾客)完全不知道餐馆的运行机制,不知道点的餐是厨师做的还是服务员做的或者是从隔壁买的。。
3.执行者(厨师)完全不知道请求者的情况,它只做了本职工作,其它的什么都不知道
四.命令模式的扩展
1.宏命令(多条命令顺序执行)
我们可以定义“命令的命令”来实现(这种特殊的命令的execute方法内部是顺序调用其它若干命令的execute方法。。)
2.撤销
假如来了很多顾客,点了很多份餐点,过了一会儿有几个顾客等不及了需要撤销,我们如何实现?
维护一个命令列表,记录已经创建的命令,撤销时需要找到对应的命令,执行撤销操作
当然,前提是命令对象支持撤销,我们需要做一些修改:
package CommandPattern; /**
* @author ayqy
* 定义Command接口
*/
public interface Command {
public abstract void execute();//只需要定义一个统一的执行方法 public abstract void undo();//定义统一的撤销方法
}
各个命令的撤销操作可能不同,因此定义为抽象方法,由子类来实现具体操作
*如何支持多步顺序撤销?
餐馆的例子可能不需要这样的功能,不妨想想另一个情景,文本编辑器
用户发出了一系列命令,完成了一些列操作,后来发现并不需要这样做,用户会撤销修改(Ctrl + Z),这时我们需要执行相反的操作对内容作以还原,要如何实现?
还是要先实现各个命令的undo行为(执行与execute相反顺序的操作即可),除此之外,我们还需要一个栈来记录已经被执行过的操作,以支持撤销到初始状态
3.队列请求
我们可以建立一个工作线程,负责所有运算,想象有一个通道,输入是一条条不同命令,输出是命令的执行结果
可能上一刻工作线程在做大饼,下一刻已经出去买菜了。。
*这样做有什么好处?
可以把运算限制在指定的几个线程中,加以控制
4.日志请求
多用于数据库管理系统的实现,我们需要把一系列的操作记录下来(如写在硬盘上),在遇到系统故障时读出来以恢复数据,如何实现?
利用对象的序列化把对象保存起来(记录日志),在需要的时候反序列化(恢复事务)
五.总结
命令模式可以有效地解耦请求者与执行者,还可以提供一些额外的好处(比如支持撤销操作、队列请求、记录日志等等)
设计模式之命令模式(Command Pattern)的更多相关文章
- 乐在其中设计模式(C#) - 命令模式(Command Pattern)
原文:乐在其中设计模式(C#) - 命令模式(Command Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 命令模式(Command Pattern) 作者:webabcd ...
- 二十四种设计模式:命令模式(Command Pattern)
命令模式(Command Pattern) 介绍将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化:对请求排队或记录请求日志,以及支持可取消的操作. 示例有一个Message实体类,某个 ...
- 设计模式-15命令模式(Command Pattern)
1.模式动机 在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计,使 ...
- 【UE4 设计模式】命令模式 Command Pattern
概述 描述 将一个请求封装为一个对象,从而使我们可用不同的请求对客户进行参数化:对请求排队或者记录请求日志,以及支持可撤销的操作. 命令模式是一种对象行为型模式,其别名为动作(Action)模式或事务 ...
- 设计模式 - 命令模式(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) : 将请求封装成对 ...
- 设计模式 - 命令模式(command pattern) 宏命令(macro command) 具体解释
命令模式(command pattern) 宏命令(macro command) 具体解释 本文地址: http://blog.csdn.net/caroline_wendy 參考: 命名模式(撤销) ...
- 设计模式 - 命令模式(command pattern) 撤销(undo) 具体解释
命令模式(command pattern) 撤销(undo) 详细解释 本文地址: http://blog.csdn.net/caroline_wendy 參考命令模式: http://blog.cs ...
- 设计模式 ( 十三 ) 命令模式Command(对象行为型)
设计模式 ( 十三 ) 命令模式Command(对象行为型) 1.概述 在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需 ...
- 设计模式----行为型模式之命令模式(Command Pattern)
下面来自head first设计模式的命令模式一章节. 定义 将"请求"封装成对象,以便使用不同的请求.队列或者日志来参数化其他对象.命令模式也支持可撤销的操作. 类图 注: 1. ...
随机推荐
- 常用修图工具的一些使用技巧及问题解决方法——ai
一.ai如何修改画布大小 一. ai如何修改画布大小: 1. 左上角菜单中的文件——文档设置(也可以直接点菜单栏下边的控制栏中的文档设置) 2. 文档设置界面中,点击右上角“编辑画板“ 3. 此时面板 ...
- 控制span的width属性及display属性值的选择
给span设置width样式,会发现并没有改变它的宽度,但有时候我们需要给它设置固定的宽度,那么就可以设置它的display样式,改变span的显示模式就好了. span默认显示模式是inline,无 ...
- VBA json parser[z]
http://www.ediy.co.nz/vbjson-json-parser-library-in-vb6-xidc55680.html VB-JSON: A Visual Basic 6 (VB ...
- DB2中的数据类型
DB2中的数据类型DB2内置数据类型可以分成数值型(numeric).字符串型(character string).图形字符串(graphic string).二进制字符串型(binary strin ...
- p3412 [POI2005]SKO-Knights
传送门 分析 图1 我们假设我们现在有两个向量(2,3)和(4,2),将他们所能到达的点在几何画板上画出来,再将这些点用红线连起来,在将横坐标相同的点用蓝线连起来便能得到图1,就此我们可以发现可以用绿 ...
- Zookeeper 系列(五)Curator API
Zookeeper 系列(五)Curator API 一.Curator 使用 Curator 框架中使用链式编程风格,易读性更强,使用工程方法创建连接对象使用. (1) CuratorFramewo ...
- js父子窗口传值以及当前页面在js前台如何获得url参数
1.首先是父页面用window.open打开窗口 <%@ Page Language="C#" AutoEventWireup="true" CodeBe ...
- 学习GIT 版本控制的好去处 另GDB资料
廖雪峰的官方网站 http://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000 作者不仅仅是做技 ...
- vue路由组件传参
在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性. 使用 props 将组件和路由解耦: 取代与 $route 的耦合 const ...
- 2018秋季c语言基础课第一次作业
1)大学和高中最大的不同是没有人天天看着你,请看大学理想的师生关系是?有何感想? 答:邹欣老师提到了很多种关系,不外呼就是两种:平等或者不平等.平等的师生关系与陌生人无异,而自古以来尊师重道却被世人所 ...