我们今天学习的是命令模式,虽然看起来没有什么太大的用处,而且看起来和策略模式有点类似,但今天我们要讲解一下他的设计意图和具体的区别
命令模式,翻译过来就是 Command Design Pattern
其翻译过来为,命令模式将请求封装为一个对象,这样可以使用不同的请求参数化其他的对象,将不同的请求依赖注入到其他的对象,并且能够支持请求命令的排队执行,记录日志,撤销等操作
对于GOF的定义,我们在进行一次解读
就是需要将函数封装为对象,C语言支持函数指针,我们可以把函数当做变量进行传递,但是其他的语言中没有函数指针,没有办法直接去传递函数作为参数,也没法去直接赋值,于是我们可以借助命令模式,利用抽象的特性,设计一个包含这个函数的类,实例化一个对象去传递,将函数像对象一样的使用,从实现的角度,就是类似回调
将函数抽象出来,并且可以有具体的实现对象,方便我们去控制执行,而且可以控制接受到不同的命令的执行,比如异步,延迟,存储,日志
接下来我们来具体实现一个
假如有一个游戏开发,我们需要负责后端的开发,其主要的工作就是在一局游戏结束后,将内存中的玩家操作,持久化或者处理后保存数据库中,而且这个数据的传递不一定是在一局游戏结束后进行的,而是可能在任何的时候,而且为了节省网络连接的建立开销,一般在游戏开发中都是长连接的方式去连接的,内部则是有着自定义格式的通信,比如Protocol Buffer Json XML,甚至自定义格式,不管是什么事格式,一般客户端往服务器发送的请求,往往都是包含了请求的命令和数据两个部分的
指令(命令)也可以看做是执行的动作,数据就是执行这个指令所需的数据
对于这种指令,服务器端和客户端的交互处理有两种,我们一可以利用多线程,一个主线程负责接收这些请求,每次接收到一个请求之后,启动一个新的线程来处理请求
或者利用在一个线程中轮询的处理这个请求,也就是每次请求来到的时候,先进行对应的保存到一个数据栈中,然后线程每次从栈中拿一个进行处理
我们先来一次第二种的实现方式
命令模式的命令抽象为一个接口,然后存储在内存队列中,然后从队列中依次取出命令来执行,执行完成后,继续取出
public interface Command {
void execute(); } public class GotDiamondCommand implements Command { // 省略成员变量 public GotDiamondCommand(/*数据*/) { //… } @Override public void execute() { // 执行相应的逻辑 } } //GotStartCommand/HitObstacleCommand/ArchiveCommand类省略 public class GameApplication { private static final int MAX_HANDLED_REQ_COUNT_PER_LOOP = 100; private Queue<Command> queue = new LinkedList<>(); public void mainloop() { while (true) { List<Request> requests = new ArrayList<>(); //省略从epoll或者select中获取数据,并封装成Request的逻辑, //注意设置超时时间,如果很长时间没有接收到请求,就继续下面的逻辑处理。 for (Request request : requests) { Event event = request.getEvent(); Command command = null; if (event.equals(Event.GOT_DIAMOND)) { command = new GotDiamondCommand(/*数据*/); } else if (event.equals(Event.GOT_STAR)) { command = new GotStartCommand(/*数据*/); } else if (event.equals(Event.HIT_OBSTACLE)) { command = new HitObstacleCommand(/*数据*/); } else if (event.equals(Event.ARCHIVE)) { command = new ArchiveCommand(/*数据*/); } // …一堆else if… queue.add(command); } int handledCount = 0; while (handledCount < MAX_HANDLED_REQ_COUNT_PER_LOOP) { if (queue.isEmpty()) { break; } Command command = queue.poll(); command.execute(); } } } } |
从上面看,命令模式和策略模式,工厂模式很类似啊,但是是具有本质上的区别的,每种设计模式有着独特的设计思路和应用场景的
我们但看具体的设计思路合作代码实现,很多的设计模式都是类似的,但是从设计意图来看,比如策略模式和工厂模式的区别的,策略模式更加侧重于策略或者算法的实现,根据不同的运行时状态进行不同策略的调用和使用,工厂模式更加侧重于封装对象的创建过程,这两个对象没有任何业务场景的限定,可以是策略或者任何东西
命令模式也是类似,命令模式是根据不同的命令来进行不同的处理操作,不同的处理之间不可以替换,而策略模式则是,不同的策略之间都是为了一个目的服务的,只是实现不同罢了,例如BubbleSory和SelectionSort都是为了排序,不过是利用了冒泡排序和插入排序实现的
本章重点
命令模式是一种很少用的模式,了解即可,在实际的编码实现中,命令模式用到的最核心的实现,就是讲函数封装为对象,因为一般的编程语言不支持函数的传递,所以我们只能封装为对象进行传递,而且对于不同的命令,我们可以进行不同的操作,比如异步 延迟 对象 撤销 存储 日志记录等,这就是命令模式
课后思考
请给出两个代码实现或者设计思路很类似的模式,说一下不同之处
装饰器模式和适配器模式在组合的实现时候就很相似,但是装饰器模式是为了增强原有类的功能而适配器模式虽然也是修改原有类,但是是为了补救原有的缺陷的