解释器模式
给定一个语言,定义它的文法表示,并定义一个解释器,这个解释起使用该标识来解释语言中的句子
文法:文法适用于描述语言的语法结构的形式规则
抽象语法树:是源代码一种抽象表示,它以树状的形式表现标称语言的语法结构,树上的每个节点都表示一个语法
角色:
- 抽象表达式:约定解释器的解释操作
- 终结符表达:用来实现文法中与终结符相关的操作,文法中的每一个终结符都对应于一个非终结符表达式
- 环境角色:通常包含解释器需要的数据或是公共功能
- 客户端:主要任务是将需要分析的句子或表达式转换成使用解释器对象描述的抽象语法树,然后调用解释器的解释方法
使用场景
- 当文法比较简单,且执行效率不是关键问题时
- 当问题重复出现,且可以用一种简单的语言表达时
- 当一个语言需要解释执行,并且语言中的句子可以表示为一个抽象语法树的时候
案例实现
class AbstractExpression {
constructor() {
if (new.target === AbstractExpression) {
throw new Error('abstract class cant instance');
}
}
interpret(context) {
if (this instanceof AbstractExpression) {
throw new Error('');
}
}
}
class Context {
constructor(){
this.map = new Map();
}
assign(vari, value){
this.map.set(vari, value);
}
getValue(vari){
return this.map.get(vari);
}
}
class Variable {
constructor(name) {
this.name = name;
}
interpret(context) {
// 直接返回变量的值
return context.getValue(this);
}
}
class Plus extends AbstractExpression {
constructor(left, right) {
super();
// +号左边的表达式
this.left = left;
// +号右边的表达式
this.right = right;
}
interpret(context) {
// 将左边表达式的结果和右边表达式的结果相加
return this.left.interpret(context) + this.right.interpret(context);
}
}
class Minus extends AbstractExpression {
constructor(left, right) {
super();
// -号左边的表达式
this.left = left;
// -号右边的表达式
this.right = right;
}
interpret(context) {
// 将左边表达式的结果和右边表达式的结果相减
return this.left.interpret(context) - this.right.interpret(context);
}
}
class Client {
constructor(){
let context = new Context();
let varia = new Variable('a');
let varib = new Variable('b');
let varic = new Variable('c');
let varid = new Variable('d');
context.assign(varia, 1);
context.assign(varib, 2);
context.assign(varic, 3);
context.assign(varid, 4);
let expression = new Minus(varia, new Plus(new Minus(varib, varic), varid));
this.result = expression.interpret(context);
console.log(this.result)
}
}
let cli = new Client();
优缺点
优点
- 易于改变和扩展语法,在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法,每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言
- 实现文法较为容易,在抽象语法结构中每一个表达式节点的实现都是相似的,这些类的代码的编写不会特别复杂
- 增加新的解释表达式较为容易,如果需要增加新的解释表达式只需要对应增加一个新的终结符表达式或非终结符表达式类,原有的表达式类无须更改,符合开闭原则
缺点
- 对于复杂文法难以维护,在解释器模式中,每一条规则至少需要定义一个类,因此,如果一个语言包含太多语法规则,类的个数会急剧增加,导致系统难以管理
- 执行效率低,由于在解释器模式中使用了大量的循环和递归调用,因此,在解释较为复杂的句子时速度很慢,而且代码的调试过程也比较麻烦