1. 问题引入
1.概述:command模式,将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。[GOF《设计模式》]command封装请求,使请求者与实现者松散耦合。
2.思考:请求和命令是抽象的概念,如何封装成类?通过引入command对象这一中介,虽然可以使请求者和实现者解耦,但是不也增加了两者对command对象的依赖吗?将命令封装成类,为什么会产生解耦的效果?一些案例代码中给出的command对象实际上只是一个实现者的包装而已,而command对象的Execute方法只是简单的转发请求给实现者。客户将实现者作为构造函数的参数以创建command对象,再由command对象配置请求者(Invoker)对象,再调用请求者的相关操作,请求者再依靠command对象实现请求,command又将请求转发给实现者,这样来回委托的意义何在?Invoker给人的感觉像是一个中介。
看似一个简单的概念,只知道模式的作用和效果是不行的,应该知道为何产生这样的作用和效果。
2. 案例引入
在一个基于区块的小游戏中,游戏地图按行和列均匀划分为各个不同的区块,游戏人物位于这样的区块地图上。区块有不同的种类,区块上分布着一些事件,当游戏人物走到新的区块上时,就会触发对应区块上的事件。如获得道具事件会增加player相应道具,提升能力事件会提升player相应能力,控制移动事件将强制player移动到新的区块,陷阱事件会减少player的生命值并产生晃动游戏屏幕的效果,最后会向游戏消息栏发送一条消息。这些地图区块与事件以文件的形式保存,运行程序时读取这些游戏数据,在区块上设置准确的事件指令。
现在需要简单模拟这样的情况。本文以C#语言为例进行解释。
3. 方法选择
可以想到3种方法来模拟这种情况。
1.通过C#的事件机制来模拟游戏中的事件,这是一种Observer模式(发布-订阅)的运用。有一个委托接口
public delegate void CommandHandle(Queue<GameEventArgs> datas, Hero player);
有一些 实现委托接口的静态方法,这些静态方法从datas参数队列中弹出一条数据作为实际参数,可以实现特定命令的执行。区块(Invoker)创建时,订阅这些方法,以实现参数化功能的效果。这些静态方法没有状态所以可以被共享。
这种方法可以避免Command子类的生成,但是并不能记录调用状态,也不能实现撤销等需要多接口的操作。
2.通过Command模式封装这些细粒度的指令,每一个指令对应一个Command子类。
定义一个MacroCommand类,它执行一个命令序列,由ConcreteCommand对象组合而成。Invoker只需依赖MacroCommand