Skip to content

解释器模式(Interpreter Pattern)

定义

解释器模式是一种行为设计模式,它定义了一个语言的文法,并且建立一个解释器来解释该语言中的句子。

英文原文定义

The Interpreter Pattern defines a grammatical representation for a language and an interpreter to interpret the grammar.

核心概念

解释器模式通过构建语法树和定义节点类来实现语言的解释执行,主要应用于特定类型问题的频繁解释场景。

主要角色

  1. AbstractExpression(抽象表达式):声明抽象解释操作
  2. TerminalExpression(终结符表达式):实现与文法中终结符相关的解释操作
  3. NonterminalExpression(非终结符表达式):实现文法规则的解释操作
  4. Context(上下文):包含解释器之外的全局信息
  5. Client(客户端):构建语法树并调用解释操作

优缺点

优点

  • 易于扩展文法:新增表达式类即可扩展语言
  • 实现文法简单:每个语法规则对应一个类
  • 适合领域语言:特别适合特定领域简单语言实现
  • 灵活组合:通过对象组合可方便地改变/扩展文法

缺点

  • 复杂文法难维护:文法规则很多时,类层次结构会变得庞大
  • 执行效率较低:解释型执行通常比编译型慢
  • 应用场景有限:仅适用于文法简单的语言
  • 类膨胀问题:每个规则都需要对应的类

解决的问题

解释器模式主要解决以下经典问题:

  1. 领域特定语言:需要实现简单领域特定语言(DSL)时
  2. 语法分析:当有一个语言需要解释执行时
  3. 频繁变化文法:文法可能频繁变化的场景
  4. 表达式求值:数学表达式、布尔表达式等解释执行
  5. 规则引擎:简单规则引擎的实现

实现注意事项

  1. 文法复杂度控制

    • 适合文法简单的场景
    • 复杂文法考虑使用解析器生成工具
  2. 抽象语法树构建

    • 客户端负责构建语法树
    • 可结合组合模式构建树结构
  3. 共享终结符

    • 终结符可以共享实例
    • 使用享元模式优化
  4. 解释方向

    • 递归下降解释
    • 迭代解释
  5. 错误处理

    • 设计良好的错误报告机制
    • 考虑失败安全解释

实现变体

  1. 基于组合模式

    • 使用组合模式构建表达式树
    • 统一处理叶节点和组合节点
  2. 基于访问者模式

    • 使用访问者分离文法与操作
    • 便于新增操作而不修改表达式类
  3. 基于扩展方法

    • 在支持扩展方法的语言中
    • 为表达式基类添加解释方法
  4. 动态解释器

    • 使用反射等动态技术
    • 运行时构建解释逻辑

与其他模式的关系

相似模式区分

  1. 组合模式

    • 解释器:通常用组合模式构建语法树
    • 组合:描述部分-整体层次结构
  2. 访问者模式

    • 解释器:可结合访问者分离文法与操作
    • 访问者:封装对复杂结构的操作
  3. 策略模式

    • 解释器:封装解释算法
    • 策略:封装可互换的算法

常见搭配组合

  1. 解释器 + 组合:构建抽象语法树
  2. 解释器 + 享元:共享终结符实例
  3. 解释器 + 访问者:实现多种解释操作
  4. 解释器 + 建造者:逐步构建复杂语法树

典型应用场景

  • 正则表达式引擎
  • SQL语法解析
  • 数学公式计算器
  • 业务规则引擎
  • 配置文件解析
  • 简单编程语言实现
  • 模板引擎

代码示例(布尔表达式解释器)

java
// 抽象表达式
interface BooleanExpression {
    boolean evaluate(Context context);
    BooleanExpression replace(String var, BooleanExpression exp);
    BooleanExpression copy();
}

// 终结符表达式:变量
class VariableExpression implements BooleanExpression {
    private String name;
    
    public VariableExpression(String name) {
        this.name = name;
    }
    
    @Override
    public boolean evaluate(Context context) {
        return context.lookup(name);
    }
    
    @Override
    public BooleanExpression replace(String var, BooleanExpression exp) {
        return name.equals(var) ? exp.copy() : new VariableExpression(name);
    }
    
    @Override
    public BooleanExpression copy() {
        return new VariableExpression(name);
    }
}

// 非终结符表达式:与操作
class AndExpression implements BooleanExpression {
    private BooleanExpression left;
    private BooleanExpression right;
    
    public AndExpression(BooleanExpression left, BooleanExpression right) {
        this.left = left;
        this.right = right;
    }
    
    @Override
    public boolean evaluate(Context context) {
        return left.evaluate(context) && right.evaluate(context);
    }
    
    @Override
    public BooleanExpression replace(String var, BooleanExpression exp) {
        return new AndExpression(
            left.replace(var, exp),
            right.replace(var, exp)
        );
    }
    
    @Override
    public BooleanExpression copy() {
        return new AndExpression(left.copy(), right.copy());
    }
}

// 上下文
class Context {
    private Map<String, Boolean> variables = new HashMap<>();
    
    public void assign(String var, boolean value) {
        variables.put(var, value);
    }
    
    public boolean lookup(String var) {
        Boolean value = variables.get(var);
        if (value == null) {
            throw new IllegalArgumentException();
        }
        return value;
    }
}

// 客户端代码
public class BooleanInterpreter {
    public static void main(String[] args) {
        // 构建表达式:(x AND y)
        BooleanExpression x = new VariableExpression("x");
        BooleanExpression y = new VariableExpression("y");
        BooleanExpression exp = new AndExpression(x, y);
        
        Context context = new Context();
        context.assign("x", true);
        context.assign("y", false);
        
        boolean result = exp.evaluate(context);
        System.out.println("(x AND y) = " + result); // 输出 false
    }
}