共计 2575 个字符,预计需要花费 7 分钟才能阅读完成。
Interpreter(解释器模式)
Interpreter(解释器模式)属于行为型模式。
用意:给定一个语言,定义它的文法的一种示意,并定义一个解释器。这个解释器应用该示意来解释语言中的句子。
任何一门语言,无论是日常语言还是编程语言都有明确的语法,只有有语法就能够用文法形容,并通过语法解释器将字符串的语言结构化。
举例子
如果看不懂下面的用意介绍,没有关系,设计模式须要在日常工作里用起来,联合例子能够加深你的了解,上面我筹备了三个例子,让你领会什么场景下会用到这种设计模式。
SQL 解释器
SQL 是一种描述语言,所以也实用于解释器模式。不同的 SQL 方言有不同的语法,咱们能够依据某种特定的 SQL 方言定制一套适配它的文法表达式,再利用 antlr 解析为一颗语法书。在这个例子中,antlr 就是解释器。
代码编译器
程序语言也因为其人造是字符串的起因,和 SQL、日常语言都相似,须要一种模式解析后能力工作。不同的语言有不同的文法示意,咱们只须要一个相似 antlr 的通用解释器,通过传入不同的文法示意,返回不同的对象构造。
自然语言解决
自然语言解决也是解释器的一种,首先自然语言解决个别只能解决日常语言的子集,因而先定义好反对的范畴,再定义一套分词零碎与文法表达式,并将分词后的后果传入灌入了此文法表达式的解释器,这样解释器能够返回结构化数据,依据结构化数据再进行剖析与加工。
用意解释
用意:给定一个语言,定义它的文法的一种示意,并定义一个解释器。这个解释器应用该示意来解释语言中的句子。
对于给定的语言,能够是 SQL、代码或自然语言,“定义它的文法的一种示意”即文法能够有多种示意,只需定义一种。要留神的是,不同文法执行效率会有差别。
“并定义一个解释器”,这个解释器就是相似 antlr 的货色,传给它一个文法表达式,就能够解析句子了。即:解释器 (语言, 文法) = 形象语法树。
咱们能够间接把文法定义耦合到解释器里,但这样做会导致语法简单时,解释器难以保护。比拟好的形式是定义一套与解释器解耦的文法表达式,通过预处理器最终生成解释器。
结构图
<img width=600 src=”https://img.alicdn.com/imgextra/i4/O1CN019y6R201yinq7xjJEK_!!6000000006613-2-tps-1530-776.png”>
Context 是其余上下文变量,AbstractExpression 是形象语法表达式。
能够看到,TerminalExpression(终结符)与 NonterminalExpression(非终结符) 都继承于 AbstractExpression,终结符指的是没有后续开展的符号,非终结符相同,所以非终结符又指向了 AbstractExpression,如此递归。
代码例子
上面例子应用 typescript 编写。
假如咱们要实现以下文法:
sum ::= number + number
number ::= 1 | 2
表白一个最简略的加法文法,其中加法表达式 sum 和 number 都是非终结符,而 +、1、2 是终结符。这个例子只能做到 1 与 2 的加法,通过这个简略例子,理解一下解释器模式的精华吧:
// 形象表达式
class AbstractExpression {interpret (text: string) {}}
// 终结符表达式
class TerminalExpression extends AbstractExpression {constructor(values: string[]) {this.values = values}
interpret(value: string) {
// 值必须是其中之一
return this.values.includes(value)
}
}
// 非终结符表达式
class NonterminalExpression extends AbstractExpression {constructor(left: TerminalExpression, right: TerminalExpression) {
this.left = left
this.right = right
}
interpret(value: string) {if (value.indexOf("+") === -1) {
// 必须蕴含 + 号
return false
}
const splitValue = value.split('+')
return this.left.interpret(splitValue[0])
&& this.right.interpret(splitValue[1])
}
}
// 调用
const context = new Context()
const terminal = new TerminalExpression(["1", "2"])
const add = new AddExpression(terminal, terminal)
add.interpreter("1 + 1") // true
add.interpreter("1 + 2") // true
add.interpreter("1 + 3") // false
add.interpreter("2 - 1") // false
遇到非终结符则持续调用,只有终结符能力直接判断,原理很简略。
弊病
下面的例子是比拟低效场景,因为当语法简单后,类的数目会显著增多,难以保护,此时须要用一个通用语法解析器,理解更多能够看笔者之前的文章:精读《手写 SQL 编译器 – 语法分析》系列。
总结
解释器是一种思维,将简单语法解析形象为一个个独立的终结符与非终结符各自判断,只有每个文法本人的判断做好了,剩下的工作就是组装文法。
这种将单个逻辑判断与文法组装解耦的做法,能够使逻辑判断与文法组装独立变换,使简单语法解析转化为一个个具体的简略问题。
探讨地址是:精读《设计模式 – Interpreter 解释器模式》· Issue #296 · dt-fe/weekly
如果你想参加探讨,请 点击这里,每周都有新的主题,周末或周一公布。前端精读 – 帮你筛选靠谱的内容。
关注 前端精读微信公众号
<img width=200 src=”https://img.alicdn.com/tfs/TB165W0MCzqK1RjSZFLXXcn2XXa-258-258.jpg”>
版权申明:自在转载 - 非商用 - 非衍生 - 放弃署名(创意共享 3.0 许可证)