「用类来表现」的设计模式
这两种设计模式不太常用,因此放在一个章节中介绍。标题「用类来表现」源自《图解设计模式》介绍这命令模式、解释器模式时用的章节标题。
命令模式:命令也是类

The command pattern encapsulates a request as an object, thereby letting us parameterize other objects with different requests, queue or log requests, and support undoable operations.——GoF
命令模式将请求(命令)封装为一个对象,这样可以使用不同的请求参数化其他对象(将不同请求依赖注入到其他对象),并且能够支持请求(命令)的排队执行、记录日志、撤销等(附加控制)功能。
命令将可以用「物」表示,在代码实现中,就是将函数封装为对象,这也是命令模式的核心实现手段。具体来说就是设计一个包含这个函数的类,实例化一个对象传来传去,这样就可以实现把函数像对象一样使用。

当我们把函数封装成对象之后,对象就可以存储下来,方便控制执行。所以,命令模式的主要作用和应用场景,是用来控制命令的执行,比如,异步、延迟、排队执行命令、撤销重做命令、存储命令、给命令记录日志等等;再比如,要想管理工作的历史记录,只需要管理这些命令实例的集合,并可以随时再次执行过去的命令,或者将多个过去的命令整合为一个新命令。这些是命令模式能发挥独一无二作用的地方。

课本上的示例程序中,
Invoker和Receiver是同一个类。Client新建命令时,ConcreteCommand的构造函数包含Receiver,这样Command才可以调用receiver执行命令。

登场角色:
Command(命令):Command角色负责定义命令的接口(API)。ConcreteCommand(具体的命令):ConcreteCommand角色负责实现在 Command 角色中定义的接口(API)。Receiver(接收者):Receiver角色是Command角色执行命令时的对象,也可以称其为命令接收者。Client(请求者):Client角色负责生成ConcreteCommand角色并分配Recceiver角色。Invoker(发动者):Invoker角色是开始执行命令的角色,它会调用在Command角色中定义的接口(API)。
| 优点 | 缺点 |
|---|---|
| 单一职责原则。可以解耦触发和执行操作的类。 | 代码可能会变得更加复杂,因为在发送者和接收者之间增加了一个全新的层次。 |
| 开闭原则。可以在不修改已有客户端代码的情况下在程序中创建新的命令。 | |
| 可以实现撤销和恢复功能。 | |
| 可以实现操作的延迟执行。 | |
| 可以将一组简单命令组合成一个复杂命令。 |
与其他设计模式的比较
相关的设计模式:
- 站内文章组合模式。命令的集合(它实现
Command)可以用组合模式。 Memento模式。保存Command角色的历史记录可以使用 Memento 模式。- 站内文章原型模式。优势使用原型模式复制发生的事件,保存历史记录。
- 站内文章访问者模式:可以将访问者模式视为命令模式的加强版本,其对象可对不同类的多种对象执行操作。
与 站内文章策略模式 很像,两者都能通过某些行为来参数化对象。不同点:
- 在命令模式中,不同的命令具有不同的目的,对应不同的处理逻辑,并且互相之间不可替换。使用命令来将任何操作转换为对象。操作的参数将成为对象的成员变量。你可以通过转换来延迟操作的执行、将操作放入队列、保存历史命令或者向远程服务发送命令等。
- 在策略模式中,不同的策略具有相同的目的、不同的实现、互相之间可以替换。比如,
BubbleSort、SelectionSort都是为了实现排序的,只不过一个是用冒泡排序算法来实现的,另一个是用选择排序算法来实现的。
案例
例子:手游后端服务器轮询获取客户端发来的请求,获取到请求之后,借助命令模式,把请求包含的数据和处理逻辑封装为命令对象,并存储在内存队列中。然后,再从队列中取出一定数量的命令来执行。执行完成之后,再重新开始新的一轮轮询。
1 | public interface Command { |
解释器模式:语法规则也是类

🍐⚱️:我感觉这种模式只能解决这种语言解析的问题。
🍐⚱️:单例模式简单,抽象工厂、解释器难。
解释器模式描述如何构建一个简单的「语言」解释器。
Interpreter pattern is used to defines a grammatical representation for a language and provides an interpreter to deal with this grammar.——GoF
解释器模式为某个语言定义它的语法(文法)表示,并定义一个解释器用来处理这个语法。
在解释器模式中,程序要解决的问题会被用非常简单的「迷你语言」表达出来,即用「迷你语言」编写的「迷你程序」把具体问题表述出来。
比起命令模式,解释器模式更加小众,只在一些特定的领域会被用到,比如编译器、规则引擎、正则表达式。


《图解设计模式》中例子值得学习。
登场角色
AbstractExpression(抽象表达式):AbstractExpression角色定义了语法树节点的共同接口(API)。在示例程序中,由Node类扮演此角色。TerminalExpression(终结符表达式):TerminalExpression角色对应 BNF 中的终结符表达式。NonterminalExpression(非终结符表达式):NonterminalExpression角色对应 BNF 中的非终结符表达式。Context(文脉、上下文):Context角色为解释器进行语法解析提供了必要的信息。Client(请求者):为了推导语法树,Client角色会调用TerminalExpression角色和NonterminalExpression角色。
拓展思路:
- 其他的迷你语言:正则表达式,检索表达式,批处理语言
相关的设计模式:
NonterminalExpression角色多是递归结构,因此常会使用 站内文章组合模式 来实现NonterminalExpression角色。- 有时会使用 站内文章享元模式 来共享
TerminalExpression角色。 - 在推导出语法树后,有时会使用 Visitor 模式来访问语法树的各个节点。
本文参考
- 《图解设计模式》22、23 章
- 本科生课程笔记《程序设计中级实践&设计模式》 - TJU 🍐⚱️
- 极客时间专栏 - 设计模式之美 - 王争
- 命令设计模式





