持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第16天
介绍
定义了一个解释器,来解释给定语言和文法的句子。其实质是把语言中的每个符号定义成一个(对象)类,从而把每个程序转换成一个具体的对象树。
给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。 解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。
解释器模式(Interpreter Pattern)提供了评估语言的语法或表达式的方式,它属于行为型模式。这种模式实现了一个表达式接口,该接口解释一个特定的上下文。这种模式被用在 SQL 解析、符号处理引擎等。
使用场景
-
可以将一个需要解释执行的语言中的句子表示为一个抽象语法树。
-
一些重复出现的问题可以用一种简单的语言来进行表达。
-
一个简单语法需要解释的场景。
应用实践
职责明确
- AbstrExpression: 抽象表达式,约定解释器的解释操作
- TerminalExpression: 终结符表达:用来实现文法中与终结符相关的操作,文法中的每一个终结符都对应于一个非终结符表达式
- NonterminalExpression: 要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法
- Context: 环境角色:通常包含解释器需要的数据或是公共功能
实现
class Context {
constructor() {
this._list = []; // 存放 终结符表达式
this._sum = 0; // 存放 非终结符表达式(运算结果)
}
get sum() {
return this._sum;
}
set sum(newValue) {
this._sum = newValue;
}
add(expression) {
this._list.push(expression);
}
get list() {
return [...this._list];
}
}
class PlusExpression {
interpret(context) {
if (!(context instanceof Context)) {
throw new Error("TypeError");
}
context.sum = ++context.sum;
}
}
class MinusExpression {
interpret(context) {
if (!(context instanceof Context)) {
throw new Error("TypeError");
}
context.sum = --context.sum;
}
}
//测试验证
const context = new Context();
// 依次添加: 表达式
context.add(new PlusExpression());
context.add(new MinusExpression());
//
context.list.forEach(expression => expression.interpret(context));
console.log(context.sum);
汇总
优点如下:
-
扩展性好。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
-
容易实现。在语法树中的每个表达式节点类都是相似的,所以实现其文法较为容易。
-
增加新的解释表达式较为容易,如果需要增加新的解释表达式只需要对应增加一个新的终结符表达式或非终结符表达式类,原有的表达式类无须更改,符合开闭原则
缺点如下:
- 执行效率较低。解释器模式中通常使用大量的循环和递归调用,当要解释的句子较复杂时,其运行速度很慢,且代码的调试过程也比较麻烦。
- 会引起类膨胀。解释器模式中的每条规则至少需要定义一个类,当包含的文法规则很多时,类的个数将急剧增加,导致系统难以管理与维护。
- 可应用的场景比较少。在软件开发中,需要定义语言文法的应用实例非常少,所以这种模式很少被使用到。