书中将 Command 命令模式、Interpreter 模式归纳为用类表现特殊东西的模式。 命令模式:将命令封装为一个类,分离命令调用者和命令实现者的责任,以便将命令进行储存、传递、调用、增加与管理。 解释器模式:将发生频率足够高的问题的各个实例表述为一个简单语言的句子,并构建一个解释器解释语言中的句子,其中的语法可以用类来解析。
Command 命令模式
定义
将命令封装为一个类,分离命令调用者和命令实现者的责任,以便将命令进行储存、传递、调用、增加与管理。
角色
- 抽象命令类:声明执行命令的接口
- 具体命令类:实现命令类的接口,并保有实现者,通过调用实现者实现操作。
- 实现者:被命令类调用是具体命令的真正实现者。
- 调用者:一般拥有很多命令对象,通过调用命令对象执行请求,而不直接调用实现者。
JavaScript 实现
// 实现者
class Build {
action () {
console.log('开始构建')
}
}
class Watch {
action () {
console.log('开始监听文件变化并自动构建')
}
}
// 抽象命令类
class Command {
execute () {
throw new Error('需实现 Command 类的 execute 方法')
}
}
// 具体命令类
class BuildCommand extends Command{
constructor () {
super()
this.receiver = new Build()
}
execute {
this.receiver.action()
}
}
class WatchCommand extends Command{
constructor () {
super()
this.receiver = new Watch()
}
execute {
this.receiver.action()
}
}
// 调用者
class Invoker {
constructor () {
this.cmd = null
}
setCommand (cmd) {
this.cmd = cmd
}
call () {
if (!this.cmd) return
this.cmd.execute()
}
}
// 使用
const invoker = new Invoker()
invoker.setCommand(new BuildCommand())
invoker.call()
invoker.setCommand(new WatchCommand())
invoker.call()
在谈到命令模式的定义的时候,我们还有一个目的,是实现对命令的存储和管理,在上面的代码中只体现除了对调用者和实现者的分离,那么管理如何显示呢,我们只需要对 Invoker 做少量修改。
const POOL = Symbol('cmdPool')
class Invoker {
constructor () {
this.cmd = null
this[POOL] = []
}
setCommand (cmd) {
this.cmd = cmd
}
call () {
if (!this.cmd) return
// 调用前存储命令对象
this[POOL].push(cmd)
this.cmd.execute()
}
replay () {
// 以实现重播、删除等功能
this[POOL].forEach(cmd => cmd.execute)
}
}
Interpreter 解释器模式
定义
将发生频率足够高的问题的各个实例表述为一个简单语言的句子,并构建一个解释器解释语言中的句子。
角色
- 抽象表达式:定义了语法树节点的共同接口,约定解释器的解释动作。
- 终结符表达式:抽象表达式的子类,实现文法中与终结相关的操作。
- 非终结符表达式:抽象表达式的子类,实现文法中与终结不相关的操作。
- 环境:为解释器进行语法解析提供必要信息。
- 客户端:调用解释器的解释方法
JavaScript实现
解释器模式用的实在是不多,做一个很好地实现实在是麻烦且浪费时间。
简要说明下,就是定义不同的语法,如,repeat、end 关键字,然后对同类关键字声明一个类,对语句的解析过程即对语句从前到后进行“阅读”,阅读的过程中将读到的关键字交给对应的类进行解析,此分发过程通常由一个通用的分发类进行。