解释器模式是一种行为设计模式,它定义了一种语言的文法表示,并提供一个解释器来解释这种语言中的句子。这种模式通常用于需要解释和执行特定领域语言的场景。

解释器模式为某个语言定义它的语法(或者叫文法)表示,并定义一个解释器用来处理这个语法。实际上,这里的“语言”不仅仅指我们平时说的中、英、日、法等各种语言。从广义上来讲,只要是能承载信息的载体,我们都可以称之为“语言”,比如,古代的结绳记事、盲文、哑语、摩斯密码等。

要想了解“语言”要表达的信息,我们就必须定义相应的语法规则。这样,书写者就可以根据语法规则来书写“句子”(专业点的叫法应该是“表达式”),阅读者根据语法规则来阅读“句子”,这样才能做到信息的正确传递。而我们要讲的解释器模式,其实就是用来实现根据语法规则解读“句子”的解释器。

解释器模式的代码实现比较灵活,没有固定的模板。我们前面说过,应用设计模式主要是应对代码的复杂性,解释器模式也不例外。它的代码实现的核心思想,就是将语法解析的工作拆分到各个小类中,以此来避免大而全的解析类。一般的做法是,将语法规则拆分一些小的独立的单元,然后对每个单元进行解析,最终合并为对整个语法规则的解析。

1. 核心概念

解释器模式包含以下主要角色:

  1. 抽象表达式 (AbstractExpression)​​:声明一个所有具体表达式都需要实现的解释接口

  2. 终结符表达式 (TerminalExpression)​​:实现与文法中的终结符相关的解释操作

  3. 非终结符表达式 (NonterminalExpression)​​:实现文法规则的解释操作,通常包含对其他表达式的引用

  4. 上下文 (Context)​​:包含解释器之外的全局信息

  5. 客户端 (Client)​​:构建表示特定文法规则的抽象语法树,调用解释操作

2. 模式结构

classDiagram
    class AbstractExpression {
        <<interface>>
        +Interpret(Context) void
    }
    
    class TerminalExpression {
        +Interpret(Context) void
    }
    
    class NonterminalExpression {
        -expression: AbstractExpression
        +Interpret(Context) void
    }
    
    class Context {
        -input: string
        -output: string
        +GetInput() string
        +SetInput(string) void
        +GetOutput() string
        +SetOutput(string) void
    }
    
    AbstractExpression <|-- TerminalExpression
    AbstractExpression <|-- NonterminalExpression
    NonterminalExpression --> AbstractExpression
    TerminalExpression --> Context
    NonterminalExpression --> Context

3. 代码示例

3.1 基本实现 - 布尔表达式解释器

// 抽象表达式
public interface IExpression
{
    bool Interpret(Context context);
}

// 上下文
public class Context
{
    private Dictionary<string, bool> _variables = new Dictionary<string, bool>();
    
    public bool GetValue(string variableName)
    {
        return _variables.TryGetValue(variableName, out var value) ? value : false;
    }
    
    public void SetVariable(string variableName, bool value)
    {
        _variables[variableName] = value;
    }
}

// 终结符表达式 - 变量
public class VariableExpression : IExpression
{
    private string _name;
    
    public VariableExpression(string name)
    {
        _name = name;
    }
    
    public bool Interpret(Context context)
    {
        return context.GetValue(_name);
    }
}

// 非终结符表达式 - AND
public class AndExpression : IExpression
{
    private IExpression _expr1;
    private IExpression _expr2;
    
    public AndExpression(IExpression expr1, IExpression expr2)
    {
        _expr1 = expr1;
        _expr2 = expr2;
    }
    
    public bool Interpret(Context context)
    {
        return _expr1.Interpret(context) && _expr2.Interpret(context);
    }
}

// 非终结符表达式 - OR
public class OrExpression : IExpression
{
    private IExpression _expr1;
    private IExpression _expr2;
    
    public OrExpression(IExpression expr1, IExpression expr2)
    {
        _expr1 = expr1;
        _expr2 = expr2;
    }
    
    public bool Interpret(Context context)
    {
        return _expr1.Interpret(context) || _expr2.Interpret(context);
    }
}

// 非终结符表达式 - NOT
public class NotExpression : IExpression
{
    private IExpression _expr;
    
    public NotExpression(IExpression expr)
    {
        _expr = expr;
    }
    
    public bool Interpret(Context context)
    {
        return !_expr.Interpret(context);
    }
}

// 客户端
class Program
{
    static void Main()
    {
        var context = new Context();
        context.SetVariable("A", true);
        context.SetVariable("B", false);
        context.SetVariable("C", true);
        
        // 构建表达式: (A AND B) OR (NOT C)
        var expr = new OrExpression(
            new AndExpression(new VariableExpression("A"), new VariableExpression("B")),
            new NotExpression(new VariableExpression("C"))
        );
        
        bool result = expr.Interpret(context);
        Console.WriteLine($"表达式结果: {result}"); // 输出: False
    }
}

3.2 更复杂的例子 - 数学表达式解释器

// 数学表达式解释器
public interface IMathExpression
{
    int Interpret(MathContext context);
}

public class MathContext
{
    public Dictionary<string, int> Variables { get; } = new Dictionary<string, int>();
}

// 数字表达式
public class NumberExpression : IMathExpression
{
    private int _number;
    
    public NumberExpression(int number)
    {
        _number = number;
    }
    
    public int Interpret(MathContext context)
    {
        return _number;
    }
}

// 变量表达式
public class VariableMathExpression : IMathExpression
{
    private string _variableName;
    
    public VariableMathExpression(string variableName)
    {
        _variableName = variableName;
    }
    
    public int Interpret(MathContext context)
    {
        return context.Variables[_variableName];
    }
}

// 加法表达式
public class AddExpression : IMathExpression
{
    private IMathExpression _left;
    private IMathExpression _right;
    
    public AddExpression(IMathExpression left, IMathExpression right)
    {
        _left = left;
        _right = right;
    }
    
    public int Interpret(MathContext context)
    {
        return _left.Interpret(context) + _right.Interpret(context);
    }
}

// 客户端使用
class MathClient
{
    static void Main()
    {
        var context = new MathContext();
        context.Variables["x"] = 5;
        context.Variables["y"] = 10;
        
        // 构建表达式: x + (y + 2)
        var expr = new AddExpression(
            new VariableMathExpression("x"),
            new AddExpression(
                new VariableMathExpression("y"),
                new NumberExpression(2)
            )
        );
        
        int result = expr.Interpret(context);
        Console.WriteLine($"计算结果: {result}"); // 输出: 17
    }
}

作者实现:一个游戏场景案例

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Xml;

namespace DesignPatternsPractice.Src.Behavioral.InterpreterDesignPattern
{
    /// <summary>
    /// 解释器模式
    /// 
    /// 实现案例
    /// 模拟游戏中的技能效果解析系统。
    /// <code> dsd<code/>
    /// </summary>
    public class Interpreter
    {
        static void Main()
        {
            // 创建玩家上下文
            var player = new PlayerContext(health: 100, mana: 50, attack: 20, defense: 10);

            //创建技能解析器
            var parser = new SkillParser();

            //定义几个技能效果 

            ISkillExpression smartHeal = parser.Parse("if health 50 add health 30 add health 10");
            // ISkillExpression simpleHeal = parser.Parse("add health 10");
            // ISkillExpression attackBasedDamage = parser.Parse("mul attack 2");
            // ISkillExpression complexSkill = parser.Parse("sub mul attack 3 defense");

            // 测试技能效果
            Console.WriteLine($"玩家初始状态: 生命={player.Health}, 魔法={player.Mana}, 攻击={player.Attack}, 防御={player.Defense}");

            // Console.WriteLine("\n执行简单治疗(add health 10):");
            // int healAmount = simpleHeal.Interpret(player);
            // player.Health += healAmount;
            // Console.WriteLine($"治疗量: {healAmount}, 治疗后生命: {player.Health}");

            // Console.WriteLine("\n执行攻击相关伤害(mul attack 2):");
            // int damage = attackBasedDamage.Interpret(player);
            // Console.WriteLine($"伤害值: {damage}");

            Console.WriteLine("\n执行智能治疗(if health 50 add health 30 add health 10):");
            int smartHealAmount = smartHeal.Interpret(player);
            player.Health += smartHealAmount;
            Console.WriteLine($"治疗量: {smartHealAmount}, 治疗后生命: {player.Health}");

            // Console.WriteLine("\n执行复杂技能(sub mul attack 3 defense):");
            // int complexDamage = complexSkill.Interpret(player);
            // Console.WriteLine($"复杂技能伤害: {complexDamage}");
            
            Console.WriteLine($"玩家治疗后的状态: 生命={player.Health}, 魔法={player.Mana}, 攻击={player.Attack}, 防御={player.Defense}");
        }
    }

    /// <summary>
    /// 抽象表达式接口
    /// </summary>
    public interface ISkillExpression
    {
        /// <summary>
        /// 解释者
        /// </summary>
        /// <returns></returns>
        public int Interpret(PlayerContext context);
    }
    /// <summary>
    /// 上下文类 保存玩家状态
    /// </summary>
    public class PlayerContext
    {
        public int Health { get; set; }
        public int Mana { get; set; }
        public int Attack { get; set; }
        public int Defense { get; set; }

        public PlayerContext(int health, int mana, int attack, int defense)
        {
            Health = health;
            Mana = mana;
            Attack = attack;
            Defense = defense;
        }
    }

    /// <summary>
    /// 数值常量表达式
    /// </summary>
    public class NumberExpression : ISkillExpression
    {
        private readonly int _number;

        public NumberExpression(int number)
        {
            _number = number;
        }
        public int Interpret(PlayerContext context)
        {
            return _number;
        }
    }
    /// <summary>
    /// 玩家属性表达式
    /// </summary>
    public class PlayerPropertyExpression : ISkillExpression
    {
        private readonly Func<PlayerContext, int> _propertySelector;
        public int Interpret(PlayerContext context)
        {
            return _propertySelector(context);
        }

        public PlayerPropertyExpression(Func<PlayerContext, int> propertySelector)
        {
            _propertySelector = propertySelector;
        }
    }

    /// <summary>
    /// 加法表达式
    /// </summary>
    public class AddExpression : ISkillExpression
    {
        private readonly ISkillExpression _left;
        private readonly ISkillExpression _right;
        public AddExpression(ISkillExpression left, ISkillExpression right)
        {
            _left = left;
            _right = right;
        }
        public int Interpret(PlayerContext context)
        {
            return _left.Interpret(context) + _right.Interpret(context);
        }
    }

    /// <summary>
    /// 减法表达式
    /// </summary>
    public class SubtractExpression : ISkillExpression
    {
        private readonly ISkillExpression _left;
        private readonly ISkillExpression _right;

        public SubtractExpression(ISkillExpression left, ISkillExpression right)
        {
            _left = left;
            _right = right;
        }

        public int Interpret(PlayerContext context)
        {
            return _left.Interpret(context) - _right.Interpret(context);
        }
    }


    /// <summary>
    /// 乘法表达式
    /// </summary>
    public class MultiplyExpression : ISkillExpression
    {
        private readonly ISkillExpression _left;
        private readonly ISkillExpression _right;

        public MultiplyExpression(ISkillExpression left, ISkillExpression right)
        {
            _left = left;
            _right = right;
        }

        public int Interpret(PlayerContext context)
        {
            return _left.Interpret(context) * _right.Interpret(context);
        }
    }

    /// <summary>
    /// 条件表达式
    /// </summary>
    public class ConditionExpression : ISkillExpression
    {
        private readonly ISkillExpression _condition;
        private readonly ISkillExpression _trueExpression;
        private readonly ISkillExpression _falseExpression;

        public ConditionExpression(ISkillExpression condition,
                                 ISkillExpression trueExpression,
                                 ISkillExpression falseExpression)
        {
            _condition = condition;
            _trueExpression = trueExpression;
            _falseExpression = falseExpression;
        }
        // health 50 add health 30 
        public int Interpret(PlayerContext context)
        {    //100 > 0 ? 50: 100+30 
            return _condition.Interpret(context) > 0
                ? _trueExpression.Interpret(context) 
                : _falseExpression.Interpret(context);
        }
    }

    /// <summary>
    /// 技能解析器
    /// </summary>
    public class SkillParser
    {
        // 带参数的返回值 用func<>
        private readonly Dictionary<string, Func<PlayerContext, int>> _propertyMap = new Dictionary<string, Func<PlayerContext, int>> 
        {
            { "health", c => c.Health },
            { "mana", c => c.Mana },
            { "attack", c => c.Attack },
            { "defense", c => c.Defense }
        };

        public ISkillExpression Parse(string expression)
        {
            // 遇到 空 括号分格 ,遇到空条目直接移除
            var token = expression.Split(new[] { ' ', '(', ')' }, StringSplitOptions.RemoveEmptyEntries).ToList();
            return ParseExpression(token);
        }
        

        /// <summary>
        /// 语法解析
        /// </summary>
        /// <param name="tokens"></param>
        /// <returns></returns>
        /// <exception cref="ArgumentException"></exception>
        public ISkillExpression ParseExpression(List<string> tokens)
        {
            if (tokens.Count == 0)
                throw new ArgumentException("空的表达式");

            string token = tokens[0];
            tokens.RemoveAt(0); // 移除顶部元素

            switch (token.ToLower()) // 转换成小写
            { 
                // 处理运算符  
                case "add":
                    return new AddExpression(ParseExpression(tokens), ParseExpression(tokens));
                case "sub":
                    return new SubtractExpression(ParseExpression(tokens), ParseExpression(tokens));
                case "mul":
                    return new MultiplyExpression(ParseExpression(tokens), ParseExpression(tokens));
                case "if":
                    var condition = ParseExpression(tokens);//
                    var trueExpr = ParseExpression(tokens); //
                    var falseExpr = ParseExpression(tokens); //
                    return new ConditionExpression(condition, trueExpr, falseExpr);

                default:
                    // 返回数字表达式
                    if (int.TryParse(token, out int number))
                        return new NumberExpression(number);
                    // 从映射表中 返回 玩家状态
                    if (_propertyMap.ContainsKey(token.ToLower()))
                        return new PlayerPropertyExpression(_propertyMap[token.ToLower()]);
                    throw new ArgumentException($"未知的 token: {token}");
            }
        }
    }

}

4. 解释器模式的优点

  1. 易于扩展文法​:添加新的表达式类即可扩展语言

  2. 实现文法简单​:类与文法规则一一对应,易于实现

  3. 易于改变解释方式​:通过修改解释器类来改变解释行为

  4. 适合领域特定语言(DSL)​​:特别适合实现简单的领域特定语言

5. 适用场景

  1. 需要解释执行一种语言的场景

  2. 文法相对简单的场景(复杂文法可能难以维护)

  3. 效率不是关键因素的场景(解释器模式通常效率不高)

  4. 特定领域语言(DSL)的实现

  5. 规则引擎的实现

  6. SQL解析、正则表达式等

6. 实际应用案例

  1. 正则表达式引擎​:解释和匹配正则表达式

  2. SQL解析器​:解析和执行SQL语句

  3. 规则引擎​:解释和执行业务规则

  4. 编译器前端​:语法分析和语义分析

  5. 计算器应用​:解析和计算数学表达式

  6. 配置文件解析​:解释特定格式的配置文件

7. 注意事项

  1. 复杂度限制​:解释器模式适合简单的文法,复杂文法会导致类数量爆炸

  2. 性能考虑​:解释执行通常比直接执行代码慢

  3. 可维护性​:每个规则对应一个类,规则多时代码量大

  4. 扩展性​:添加新规则需要修改现有代码,可能违反开闭原则

解释器模式为特定领域的问题提供了一种优雅的解决方案,但在使用时需要权衡其优缺点,确保它适合你的特定需求。