Design Pattern - 命令模式
一般执行一个操作的过程, 创建对象, 并调用对象的函数, 函数执行, 返回
比如下面的类图, client直接调用Receiver.action
而命令模式, 抽象出command对象, 并在command对象封装对Receiver.action的调用
而client只负责创建command对象(invocation), 并提交给Invoker(通过setCommand), 而command真正的执行(execution)由Invoker控制
从而实现invocation和invoker的分离和解耦合
当然会问, 直接调用那么简单, 为什么要绕那么大的圈来用command?
a. 简化调用过程, 整个调用过程比较繁杂或调用前后需要进行某些额外处理,比如日志,缓存,记录历史操作等
b. 作为"CallBack"在面向对象系统中的替代
c. 支持undo, redo
d. 更关键的是, 命令模式往往会用于异步或并发处理模式
比如, producer和consumer模式, producer可以不断产生command放到queue里面, 然后可以通过consumer来异步的执行, 达到并发和异步
命令模式 (Command), 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
这个模式的基本思路是解耦合和责任单一, “行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的, 所以要使用命令模式来解耦合.
场景, 这个模式在平时用的很多, 编辑器里面很常用的'undo/redo', 就是用的这种模式.
经常举的例子就是点菜, 客人可以直接对厨师进行点菜, 这样做有两点不好
1. 客户和厨师紧耦合, 比如厨师换了, 或菜名换了等等, 客户代码需要跟着改变.
2. 厨师除了要烹饪外, 还需要记录每个客户请求, 当客户比较多时, 且客户请求是会变化的, 换个菜, 退个菜, 这个厨师就忙不过来了, 很容易记错. 这就违反了单一责任原则, 厨师就应该负责烹饪, 其他的事应该有专门的waiter负责.
这就是命令模式, 当你需要对请求者的请求(函数调用)进行记录, undo, redo等操作时, 需要单独抽象出Invoker类来处理, 而不要直接耦合在Receiver中.
上面的例子中, 客户是Client, 服务生是Invoker, 厨师是Receiver, 客户请求是Command

class Receiver //最终命令的执行者, 厨师
{
public void Action()
{
//具体的命令执行, 烹饪菜肴
}
} class ConcreteCommand : Command //具体的客户请求, 点一道菜
{
protected Receiver rec; //该情况的执行者, 这道菜哪个厨师烧
public Command(Receiver rec)
{
this.rec = rec;
} public void Execute()
{
rec.Action()//让执行者执行命令
}
} class Invoker //Command模式的核心, 客户请求的管理者, 服务生waiter
{
private List<Command> cmds = new List<Command>(); //用于保存客户请求 public void SetOrder(Command cmd)
{
if Valid(cmd) //需要判断请求是否合理, 比如是否有这道菜
{
cmds.Add(cmd); //增加请求
}
} public void CancelOrder(Command cmd)
{
cmds.Remove(cmd); //删除请求
} public void Execute() //执行所有请求
{
for cmd in cmds
{
cmd.Excute();
}
}
}
public class Client {
public void client(){
//创建接收者
Receiver receiver = new Receiver();
//创建命令对象,设定它的接收者
Command command = new ConcreteCommand(receiver);
//创建Invoker,把命令对象设置进去
Invoker invoker = new Invoker();
invoker.SetOrder(command);
}
}Invoker具体的实现可能变化多端, 但是本质就是通过Invoke来把客户请求抽象成command, 并且保存和管理commands, 向用户提供如do, redo, undo, rollback等操作, 而这些操作对receiver都是透明的, 这取决于客户和receiver的解耦.
Design Pattern - 命令模式的更多相关文章
- Thinking In Design Pattern——MVP模式演绎
原文<Thinking In Design Pattern——MVP模式演绎>不知为何丢失了,故重新整理了一遍. 目录 What Is MVP Domain Model StubRepos ...
- [Design Pattern] Command Pattern 命令模式
发现公司的代码好像有用到 Command Pattern,回顾重温下. Command Pattern 的类图结构如下: 参考 <Head First Design Patterns(英文版)& ...
- Command Pattern 命令模式
定义: 命令模式将‘请求’封装成对象,以便使用不同的请求,队列或者日志来参数化其他对象,命令模式也支持可撤销的操作. 类图 如上图所示:Command类是用来声明执行操作的接口:ConcreteCom ...
- Command Pattern -- 命令模式原理及实现(C++)
主要参考<大话设计模式>和<设计模式:可复用面向对象软件的基础>两本书.本文介绍命令模式的实现. What it is:Encapsulate a request as an ...
- (Command Pattern)命令模式
定义 将“请求”封装成对象,以便使用不同的请求.队列或者日志来参数化其他对象.命令模式也支持可撤销的操作. 结构图: 命令模式的角色划分: Reciever(命令的接收者):接收命令,并知道如何进行必 ...
- Design Pattern - 访问者模式
访问者模式 访问者模式(Visitor), 表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作. 这个模式相对比较复杂, 而又很少能被用上, 拿G ...
- 使用PHP实现命令模式(转)
<?php /** * 命令模式 2010-08-21 sz * @author phppan.p#gmail.com http://www.phppan.com * 哥学社成员(http:// ...
- Command and Query Responsibility Segregation (CQRS) Pattern 命令和查询职责分离(CQRS)模式
Segregate operations that read data from operations that update data by using separate interfaces. T ...
- 深入浅出设计模式——命令模式(Command Pattern)
模式动机 在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计,使得请 ...
随机推荐
- Java接口的异常设计
一.问题的提出 疑惑1:在设计接口的时,对于接口方法何时需要声明抛出受检异常或者说所有的接口方法最后都声明抛出受检异常? 以下是代码片段: public interface xx{ public ...
- gcc,一个神奇的编译器
gcc是“GNU Compiler Collection”的缩写,从字面意思可以知道它是一个编译器集.gcc不止可以编译器c语言,还能用于c++,java,object-C等语言程序.但是在这里,我们 ...
- C# 一个长度为100的int数组,插入1-100的随机数,不能重复,如何写
int[] intArr = new int[100]; ArrayList myList = new ArrayList(); Random rnd = new Random(); while (m ...
- lua工具库penlight--06数据(一)
这篇太长了,分了两部分.(这个是机器翻译之后我又校对了一下,以后的都这样,人工翻译太累了.) 读数据文件 首先考虑清楚,你的确需要一个自定义的文件读入器吗?如果是,你能确定有能力写好吗? 正确,稳健, ...
- 打开.py文件的方法
用IDLE打开这个文件,然后按F5,系统就自动开始运行这个python程序,然后当前运行目录就跳转到这个目录了
- JQ实现吸顶效果代码
吸顶下过代码跟简单几行代码就可以了 如果滚动的军力大于100,就改变导航的定位方式,否则就默认 $(function(){ $(window).scroll(function(){ ...
- C++ 类的继承五(类继承中的static关键字)
//类继承中的static关键字 #include<iostream> using namespace std; /* 派生类中的静态成员 基类定义的静态成员,将被所有派生类共享 根据静态 ...
- 修改net基本三层 动软生产
控制层(dal) 模型层-实体类(Model) 显示层-web
- Photoshop脚本之eps转换成jpg
function saveEPS( doc, saveFile ) { var saveOptions = new JPEGSaveOptions( ); saveOptions.encoding = ...
- 【C++自我精讲】基础系列三 重载
[C++自我精讲]基础系列三 重载 0 前言 分二部分:函数重载,操作符重载. 1 函数重载 函数重载:指在同一名字空间中,函数名称相同,参数类型.顺序或数量不同的一类函数,同一函数名的函数能完成不同 ...