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 等语法分析工具,而非手动实现解释器。

相关推荐
西幻凌云5 小时前
认识设计模式——工厂模式
c++·设计模式·简单工厂模式·抽象工厂模式·工厂模式
崎岖Qiu6 小时前
【设计模式笔记24】:JDK源码分析-Comparator中的「策略模式」
java·笔记·设计模式·jdk·策略模式
崎岖Qiu6 小时前
【设计模式笔记23】:长文解析-深刻理解「装饰器模式」
java·笔记·设计模式·装饰器模式
阿波罗尼亚1 天前
Head First设计模式(十四) 设计原则 其他的模式
设计模式
山风wind1 天前
设计模式-责任链模式:让请求在链条中流动直到被处理
设计模式·责任链模式
invicinble1 天前
设计模式全局预览,以及为什么会
设计模式
小股虫1 天前
让系统“杀不死”:同步与异步场景下的弹性设计模式手册
分布式·微服务·设计模式·架构·团队建设·方法论
山风wind1 天前
设计模式:状态模式详解-让对象的行为随状态改变而改变
设计模式·状态模式
__万波__1 天前
二十三种设计模式(十八)--中介者模式
java·设计模式·中介者模式
自由生长20242 天前
设计模式和设计原则-中高级架构思路-面向接口编程
设计模式