什么是解释器模式?JavaScript 实现一个解释器模式的示例
·
什么是解释器模式?
解释器模式(Interpreter Pattern)是一种行为型设计模式,它定义了一种语言的语法规则,并通过一个 “解释器” 来解释执行该语言中的句子(表达式)。
其核心思想是:将复杂的语法规则拆分为简单的 “终结符” 和 “非终结符”,通过递归组合这些符号,构建一个解释器来解析和执行表达式。这种模式适合处理简单的语法解析场景(如数学表达式计算、简单规则引擎)。
解释器模式的核心角色
- 抽象表达式(Abstract Expression):定义所有表达式的公共接口,声明解释方法(如
interpret(context))。 - 终结符表达式(Terminal Expression):表示语法中的不可再分的基本符号(如数字、变量),实现抽象表达式的解释方法。
- 非终结符表达式(Non-terminal Expression):表示语法中的组合符号(如运算符
+、-),包含其他表达式(终结符或非终结符),通过递归解释子表达式完成自身逻辑。 - 上下文(Context):存储解释器需要的全局信息(如变量映射表),供表达式解释时使用。
- 客户端(Client):构建表达式树(语法树),调用解释方法执行表达式。
JavaScript 实现解释器模式示例:简单数学表达式解析
以下以 “加减数学表达式” 为例,实现解释器模式。场景是:解析并计算形如 1 + 2 - 3 的表达式,支持整数和 +、- 运算符。
代码实现
// -------------- 1. 抽象表达式(Abstract Expression)--------------
class Expression {
/**
* 解释表达式并返回结果
* @param {Context} context - 上下文对象
* @returns {number} 计算结果
*/
interpret(context) {
throw new Error("子类必须实现 interpret 方法");
}
}
// -------------- 2. 终结符表达式(Terminal Expression):数字 --------------
class NumberExpression extends Expression {
constructor(value) {
super();
this.value = value; // 存储数字值(如 1、2)
}
// 解释:直接返回数字值
interpret(context) {
return this.value;
}
}
// -------------- 3. 非终结符表达式(Non-terminal Expression):运算符 --------------
// 加法表达式
class AddExpression extends Expression {
/**
* @param {Expression} left - 左表达式(如 NumberExpression 或其他运算符表达式)
* @param {Expression} right - 右表达式
*/
constructor(left, right) {
super();
this.left = left;
this.right = right;
}
// 解释:计算左表达式 + 右表达式的结果
interpret(context) {
return this.left.interpret(context) + this.right.interpret(context);
}
}
// 减法表达式
class SubtractExpression extends Expression {
constructor(left, right) {
super();
this.left = left;
this.right = right;
}
// 解释:计算左表达式 - 右表达式的结果
interpret(context) {
return this.left.interpret(context) - this.right.interpret(context);
}
}
// -------------- 4. 上下文(Context):存储全局信息(此处简单处理,无变量)--------------
class Context {
constructor() {
// 若支持变量,可在此存储 { 变量名: 值 } 的映射,如 { a: 10, b: 20 }
}
}
// -------------- 5. 客户端工具:构建表达式树(语法解析器)--------------
class ExpressionParser {
/**
* 解析表达式字符串,构建表达式树
* @param {string} expression - 表达式字符串(如 "1 + 2 - 3")
* @returns {Expression} 根表达式(非终结符)
*/
parse(expression) {
// 分割表达式为 tokens(数字和运算符),如 ["1", "+", "2", "-", "3"]
const tokens = expression.split(/\s+/);
if (tokens.length === 0) {
throw new Error("空表达式");
}
// 初始表达式为第一个数字
let result = new NumberExpression(parseInt(tokens[0], 10));
// 遍历后续 tokens,构建运算符表达式(左结合)
for (let i = 1; i < tokens.length; i += 2) {
const operator = tokens[i];
const number = new NumberExpression(parseInt(tokens[i + 1], 10));
switch (operator) {
case "+":
result = new AddExpression(result, number);
break;
case "-":
result = new SubtractExpression(result, number);
break;
default:
throw new Error(`不支持的运算符:${operator}`);
}
}
return result;
}
}
// -------------- 6. 客户端使用 --------------
// 创建上下文和解析器
const context = new Context();
const parser = new ExpressionParser();
// 解析并计算表达式
function calculate(expression) {
const exprTree = parser.parse(expression);
const result = exprTree.interpret(context);
console.log(`${expression} = ${result}`);
}
// 测试表达式
calculate("1 + 2"); // 输出:1 + 2 = 3
calculate("5 - 3 + 4"); // 输出:5 - 3 + 4 = 6
calculate("10 - 2 - 3"); // 输出:10 - 2 - 3 = 5
calculate("7 + 8 - 5 + 2"); // 输出:7 + 8 - 5 + 2 = 12
代码说明
-
抽象表达式(Expression):定义
interpret方法,所有具体表达式必须实现该方法来解释自身。 -
终结符表达式(NumberExpression):
- 表示表达式中的数字(如
1、2),是语法中不可再分的基本单位。 interpret方法直接返回自身存储的数字值,无需依赖其他表达式。
- 表示表达式中的数字(如
-
非终结符表达式(AddExpression、SubtractExpression):
- 表示运算符(
+、-),包含左、右两个子表达式(可以是数字或其他运算符表达式)。 interpret方法通过递归调用子表达式的interpret方法,获取子结果后执行自身运算(如加法、减法)。
- 表示运算符(
-
上下文(Context):此处简化处理,未存储变量(若扩展支持变量,可在此存储变量名与值的映射,供表达式解析时查询)。
-
表达式解析器(ExpressionParser):
- 负责将表达式字符串(如
"5 - 3 + 4")解析为表达式树(语法树)。 - 解析逻辑:先分割字符串为 tokens(数字和运算符),再通过循环构建嵌套的运算符表达式(如
SubtractExpression(5, AddExpression(3, 4)))。
- 负责将表达式字符串(如
-
执行流程:客户端调用
calculate时,先通过解析器构建表达式树,再调用根表达式的interpret方法,递归计算最终结果。
解释器模式的优势
- 灵活性高:语法规则通过类的组合实现,新增语法(如乘法
*)只需添加新的非终结符表达式(MultiplyExpression),无需修改现有代码,符合 “开闭原则”。 - 易于实现简单语法:对于简单语法(如示例中的加减运算),结构清晰,实现难度低。
- 可扩展性:可通过上下文存储额外信息(如变量、函数),扩展表达式的功能(如支持
a + b其中a=10、b=20)。
适用场景与局限性
- 适用场景:
- 简单语法解析(如配置文件解析、数学表达式计算)。
- 规则引擎(如简单的业务规则判断,如
(age > 18) && (score > 60))。
- 局限性:
- 复杂语法会导致类爆炸(每个语法规则对应一个类),维护困难(如 SQL 解析不适合用解释器模式)。
- 递归调用可能影响性能,不适合处理超长表达式。
解释器模式的核心是 “拆分语法,递归解释”,通过将语法规则映射为类的层次结构,实现对自定义语言的解析和执行。它适合处理简单、固定的语法场景,是构建小型解析器的有效方案。
更多推荐
所有评论(0)