115、23种设计模式之解释器模式(22/23)

一、解释器模式(Interpreter Pattern)定义

解释器模式是行为型设计模式的一种,它定义了一个语言的文法表示,并定义一个解释器来解释该语言中的句子。核心思想是将特定问题的语法(或规则)抽象为一套解释器,通过解释器来解析和执行符合该语法的表达式。

官方定义:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
核心角色

(1)抽象表达式(Abstract Expression):声明解释操作的接口,包含解释方法 Interpret()。

(2)终结符表达式(Terminal Expression):实现文法中终结符的解释操作,是语法中最基本的单元,无后续元素。

(3)非终结符表达式(Nonterminal Expression):实现文法中非终结符的解释操作,通常包含多个子表达式,通过递归调用子表达式的解释方法完成整体解释。

(4)环境(Context):存储解释器的全局信息,供解释器使用(如变量、状态等)。

(5)客户端(Client):构建抽象语法树(AST),调用解释器执行解释操作。

二、应用场景

解释器模式适用于需要解析固定语法规则的场景,典型场景包括:

(1)简单语言 / 表达式解析:如四则运算表达式、正则表达式、SQL 解析、配置文件解析。

(2)规则引擎:如业务规则校验(如 "消费满 1000 减 100")、工作流引擎的规则解析。

(3)自定义脚本 / DSL(领域特定语言):如自定义的简易脚本语言解释器。

(4)编译器 / 解释器核心模块:如简单计算器、公式计算工具。
适用条件

  • 文法规则简单(复杂文法会导致解释器类爆炸,需结合语法分析器生成器);
  • 文法需要频繁扩展或修改(解释器模式易于扩展新的语法规则)。

三、优缺点

1.优点

(1)可扩展性强:新增语法规则只需新增对应的终结符 / 非终结符表达式类,符合 "开闭原则"。

(2)语法规则清晰:每个语法规则对应一个类,结构清晰,易于理解和维护。

(3)简化复杂逻辑:将复杂的语法解析拆分为多个简单的解释器,降低整体复杂度。

2.缺点

(1)类膨胀问题:文法规则越多,对应的解释器类越多,可能导致系统类数量急剧增加。

(2)效率较低:递归调用 + 多次循环解释,复杂文法下性能较差(如嵌套表达式)。

(3)仅适用于简单文法:复杂文法(如包含循环、分支)会导致解释器结构过于复杂,不如使用语法分析器生成器(如 ANTLR)。

四、C# 代码实例(四则运算解释器)

以下实现一个简单的四则运算解释器,支持整数的加减乘除运算(文法规则:表达式 = 数字 | 表达式 运算符 表达式)。

步骤 1:定义抽象表达式(Abstract Expression)

csharp 复制代码
/// <summary>
/// 抽象表达式:定义解释操作接口
/// </summary>
public abstract class Expression
{
    /// <summary>
    /// 解释方法:返回表达式计算结果
    /// </summary>
    /// <param name="context">环境(存储变量/状态)</param>
    /// <returns>计算结果</returns>
    public abstract int Interpret(Context context);
}

步骤 2:定义环境类(Context)

csharp 复制代码
/// <summary>
/// 环境类:存储解释器的全局信息(此处存储变量映射)
/// </summary>
public class Context
{
    // 变量名 -> 变量值(如 "a" -> 10)
    private Dictionary<string, int> _variables = new Dictionary<string, int>();

    /// <summary>
    /// 设置变量值
    /// </summary>
    public void SetVariable(string name, int value)
    {
        if (_variables.ContainsKey(name))
            _variables[name] = value;
        else
            _variables.Add(name, value);
    }

    /// <summary>
    /// 获取变量值
    /// </summary>
    public int GetVariable(string name)
    {
        if (_variables.TryGetValue(name, out int value))
            return value;
        throw new KeyNotFoundException($"变量 {name} 未定义");
    }
}

步骤 3:实现终结符表达式(数字 / 变量)

csharp 复制代码
/// <summary>
/// 终结符表达式:变量表达式(如 a、b)
/// </summary>
public class VariableExpression : Expression
{
    private readonly string _variableName;

    public VariableExpression(string variableName)
    {
        _variableName = variableName;
    }

    public override int Interpret(Context context)
    {
        // 从环境中获取变量值
        return context.GetVariable(_variableName);
    }
}

/// <summary>
/// 终结符表达式:常量表达式(如 10、20)
/// </summary>
public class ConstantExpression : Expression
{
    private readonly int _value;

    public ConstantExpression(int value)
    {
        _value = value;
    }

    public override int Interpret(Context context)
    {
        // 常量直接返回值,无需环境
        return _value;
    }
}

步骤 4:实现非终结符表达式(加减乘除)

csharp 复制代码
/// <summary>
/// 非终结符表达式:加法表达式
/// </summary>
public class AddExpression : Expression
{
    private readonly Expression _leftExpr;
    private readonly Expression _rightExpr;

    public AddExpression(Expression leftExpr, Expression rightExpr)
    {
        _leftExpr = leftExpr;
        _rightExpr = rightExpr;
    }

    public override int Interpret(Context context)
    {
        // 递归解释左右表达式,再执行加法
        return _leftExpr.Interpret(context) + _rightExpr.Interpret(context);
    }
}

/// <summary>
/// 非终结符表达式:减法表达式
/// </summary>
public class SubtractExpression : Expression
{
    private readonly Expression _leftExpr;
    private readonly Expression _rightExpr;

    public SubtractExpression(Expression leftExpr, Expression rightExpr)
    {
        _leftExpr = leftExpr;
        _rightExpr = rightExpr;
    }

    public override int Interpret(Context context)
    {
        return _leftExpr.Interpret(context) - _rightExpr.Interpret(context);
    }
}

/// <summary>
/// 非终结符表达式:乘法表达式
/// </summary>
public class MultiplyExpression : Expression
{
    private readonly Expression _leftExpr;
    private readonly Expression _rightExpr;

    public MultiplyExpression(Expression leftExpr, Expression rightExpr)
    {
        _leftExpr = leftExpr;
        _rightExpr = rightExpr;
    }

    public override int Interpret(Context context)
    {
        return _leftExpr.Interpret(context) * _rightExpr.Interpret(context);
    }
}

/// <summary>
/// 非终结符表达式:除法表达式
/// </summary>
public class DivideExpression : Expression
{
    private readonly Expression _leftExpr;
    private readonly Expression _rightExpr;

    public DivideExpression(Expression leftExpr, Expression rightExpr)
    {
        _leftExpr = leftExpr;
        _rightExpr = rightExpr;
    }

    public override int Interpret(Context context)
    {
        int rightValue = _rightExpr.Interpret(context);
        if (rightValue == 0)
            throw new DivideByZeroException("除数不能为0");
        return _leftExpr.Interpret(context) / rightValue;
    }
}

步骤 5:客户端调用(构建语法树并解释)

csharp 复制代码
class Program
{
    static void Main(string[] args)
    {
        // 1. 创建环境,定义变量
        Context context = new Context();
        context.SetVariable("a", 10);
        context.SetVariable("b", 5);

        // 2. 构建语法树:计算 (a + b) * (a - b) / 5
        // 拆解:
        // 左子树:a + b → AddExpression(Variable(a), Variable(b))
        // 右子树:a - b → SubtractExpression(Variable(a), Variable(b))
        // 乘法:MultiplyExpression(左子树, 右子树)
        // 除法:DivideExpression(乘法结果, Constant(5))

        Expression addExpr = new AddExpression(
            new VariableExpression("a"),
            new VariableExpression("b")
        );

        Expression subExpr = new SubtractExpression(
            new VariableExpression("a"),
            new VariableExpression("b")
        );

        Expression mulExpr = new MultiplyExpression(addExpr, subExpr);
        Expression divExpr = new DivideExpression(mulExpr, new ConstantExpression(5));

        // 3. 解释执行
        int result = divExpr.Interpret(context);
        Console.WriteLine($"(a + b) * (a - b) / 5 = {result}"); // 输出:(a + b) * (a - b) / 5 = 15
    }
}

输出结果

csharp 复制代码
(a + b) * (a - b) / 5 = 15

五、总结

解释器模式的核心是将语法规则拆解为可复用的解释器类,适合简单文法的解析场景。但需注意:

  • 复杂文法(如嵌套多层、循环)不建议使用,否则会导致类数量爆炸;
  • 性能敏感场景需谨慎,递归解释可能导致效率低下;
  • 扩展新语法规则时,只需新增对应的表达式类,符合开闭原则。

实际开发中,简单场景可直接使用解释器模式,复杂场景建议结合 ANTLR、Razor 等语法分析工具,而非手动实现解释器。

相关推荐
AM越.3 小时前
Java设计模式超详解--单例设计模式(含uml图)
java·设计模式·uml
会员果汁4 小时前
1.设计模式-简单工厂模式
设计模式·简单工厂模式
qq_479875436 小时前
C++ 网络编程中的 Protobuf 消息分发 (Dispatcher) 设计模式
网络·c++·设计模式
ZouZou老师12 小时前
C++设计模式之装饰器模式:以家具生产为例
c++·设计模式·装饰器模式
ZouZou老师12 小时前
C++设计模式之桥接模式:以家具生产为例
c++·设计模式·桥接模式
ZouZou老师13 小时前
C++设计模式之组合模式:以家具生产为例
c++·设计模式·组合模式
ZouZou老师17 小时前
C++设计模式之适配器模式:以家具生产为例
java·设计模式·适配器模式
明洞日记20 小时前
【设计模式手册017】备忘录模式 - 对象状态保存与恢复
c++·设计模式·备忘录模式
ZouZou老师1 天前
C++设计模式之代理模式:以家具生产示例
c++·设计模式·代理模式