📒解释器模式(Interpreter Pattern)
🏷定义:
一种行为型设计模式,用于定义一个语言的文法,并解释该语言中的表达式。
它将每个文法规则 表示为一个类,通过组合这些规则的对象来解释表达式。
🍔参与者:
- 抽象表达式(Abstract Expression):定义了一个抽象接口,声明了解释操作的方法。
- 终结符表达式(Terminal Expression):实现了抽象表达式接口,表示语言中的终结符或基本表达式。
- 非终结符表达式(Non-terminal Expression):实现了抽象表达式接口,表示语言中的非终结符或复杂表达式。通常由多个终结符和/或其他非终结符组成。
- 上下文(Context):包含需要解释的语句或表达式。
- 解释器:调用终结符和非终结符处理上下文对象,并返回解释结果或者根据结果执行动作。
🍍使用流程:
- 定义表达式的文法规则,并将其表示为抽象表达式、终结符表达式和非终结符表达式。
- 创建上下文对象 ,并将需要解释的语句或表达式传递给上下文。
- 根据语言的文法规则,逐步解释表达式。在解释器的实现中,根据不同的终结符和非终结符进行相应的处理和计算。
- 客户端通过调用解释器 来执行和处理表达式,并获取最终的结果。
🍕 优点:
- 可以简化复杂语言的解析和执行过程,提供了一种灵活且可扩展的方式来定义新的语法规则;
- 可以将语言的表达式表示为类层次结构 ,使代码更易于理解和维护;
- 支持动态添加新的解释器,从而增加语言的功能和灵活性。
🍃 缺点:
- 处理复杂的语法规则可能会导致类层次结构庞大,难以管理和维护。
- 解释器的执行效率可能较低,特别是对于大型、复杂的表达式。
🥞 示例:
typescript
// 抽象表达式类
abstract class Expression {
abstract interpret(context: Context): any;
}
class Context {
constructor(public data: string) {}
}
// 终结符表达式类:获取运算第一个数字
class GetNum1 extends Expression {
interpret(context: Context) {
const { data } = context;
const match = /如果(\d+)/.exec(data);
const num1 = match[1] - 0;
return num1;
}
}
// 终结符表达式类:获取运算第二个数字
class GetNum2 extends Expression {
interpret(context: Context) {
const { data } = context;
const match = /(\d+)等于/.exec(data);
const num2 = match[1] - 0;
return num2;
}
}
// 终结符表达式类:获取运算计算结果
class GetRst extends Expression {
interpret(context: Context) {
const { data } = context;
const match = /等于(\d+)/.exec(data);
const rst = match[1] - 0;
return rst;
}
}
// 终结符表达式类:获取收信人
class GetTarget extends Expression {
interpret(context: Context) {
const { data } = context;
const match = /发信息给(.*?)!/.exec(data);
const target = match[1];
return target;
}
}
// 终结符表达式类:获取运算符
class GetOperator extends Expression {
interpret(context: Context) {
const { data } = context;
const match = /如果\d+(.*?)\d+等于/.exec(data);
const oper = match[1].trim();
return oper;
}
}
// 解释器类
class Interpretor extends Expression {
constructor(
public getNum1: GetNum1,
public getNum2: GetNum2,
public getRst: GetRst,
public getOper: GetOperator,
public getTarget: GetTarget
) {
super();
}
interpret(context: Context) {
const num1 = this.getNum1.interpret(context);
const num2 = this.getNum2.interpret(context);
const rst = this.getRst.interpret(context);
const oper = this.getOper.interpret(context);
const target = this.getTarget.interpret(context);
let valid = false;
switch (oper) {
case "+":
valid = Math.abs(num1 + num2 - rst) < 1e-4;
break;
case "-":
valid = Math.abs(num1 - num2 - rst) < 1e-4;
break;
case "*":
valid = Math.abs(num1 * num2 - rst) < 1e-4;
break;
case "/":
valid = Math.abs(num1 / num2 - rst) < 1e-4;
break;
}
if (valid) {
console.log(`信息正确,将结果${rst}发送给收件人${target}`);
} else {
console.log(`信息错误,结果不会发送给收件人${target}`);
}
// 返回解释器翻译结果
return {
num1,
num2,
rst,
oper,
target,
};
}
}
// 使用示例
// 创建上下文实例对象
const context1 = new Context("如果10-3等于7,发信息给张三!");
const context2 = new Context("如果1 *2等于7,发信息给李四!");
// 创建终结符表达式类
const getNum1 = new GetNum1();
const getNum2 = new GetNum2();
const getRst = new GetRst();
const getOper = new GetOperator();
const getTarget = new GetTarget();
// 创建解释器实例
const interpretor = new Interpretor(
getNum1,
getNum2,
getRst,
getOper,
getTarget
);
// 使用解释器实例对象解释上下文对象
interpretor.interpret(context1);
interpretor.interpret(context2);
/*
>>>
信息正确,将结果7发送给收件人张三
信息错误,结果不会发送给收件人李四
*/
🥗 使用场景:
- 解析和执行脚本语言:浏览器通过 JavaScript 解释器来解析和执行 JavaScript 代码。这个解释器可以被视为实现了解释器设计模式的一部分,它将 JavaScript 代码转换成可执行的指令。
- 正则表达式:在 JavaScript 中,正则表达式是一种强大的工具,它通常用于匹配、搜索和替换文本。正则表达式可以被看作是一种小型的解释器,它解释并执行特定的模式规则。
- 数据查询和处理语言:在浏览器开发中,我们经常使用像 XPath、CSS 选择器或 jQuery 等工具来查询和操作 DOM 元素。这些查询语言可以被视为解释器,它们解释和执行特定的查询语句,并返回符合条件的结果集。
- 模板引擎:模板引擎用于生成动态内容,例如在前端开发中根据数据渲染 HTML 模板。一些模板引擎(如 Handlebars、Mustache 等)可能使用解释器设计模式,将模板语法解释为可执行的代码块。
- 国际化和本地化:在多语言网站开发中,解释器设计模式可以用于处理本地化字符串和格式化,例如解析日期时间格式、数字格式等。这些解释器可以将特定的本地化规则应用于字符串和数据,以生成适合特定语言环境的文本。