解释器模式是一种行为设计模式,它定义了一种语言的文法表示,并提供一个解释器来解释这种语言中的句子。这种模式通常用于需要解释和执行特定领域语言的场景。
解释器模式为某个语言定义它的语法(或者叫文法)表示,并定义一个解释器用来处理这个语法。实际上,这里的“语言”不仅仅指我们平时说的中、英、日、法等各种语言。从广义上来讲,只要是能承载信息的载体,我们都可以称之为“语言”,比如,古代的结绳记事、盲文、哑语、摩斯密码等。
要想了解“语言”要表达的信息,我们就必须定义相应的语法规则。这样,书写者就可以根据语法规则来书写“句子”(专业点的叫法应该是“表达式”),阅读者根据语法规则来阅读“句子”,这样才能做到信息的正确传递。而我们要讲的解释器模式,其实就是用来实现根据语法规则解读“句子”的解释器。
解释器模式的代码实现比较灵活,没有固定的模板。我们前面说过,应用设计模式主要是应对代码的复杂性,解释器模式也不例外。它的代码实现的核心思想,就是将语法解析的工作拆分到各个小类中,以此来避免大而全的解析类。一般的做法是,将语法规则拆分一些小的独立的单元,然后对每个单元进行解析,最终合并为对整个语法规则的解析。
1. 核心概念
解释器模式包含以下主要角色:
抽象表达式 (AbstractExpression):声明一个所有具体表达式都需要实现的解释接口
终结符表达式 (TerminalExpression):实现与文法中的终结符相关的解释操作
非终结符表达式 (NonterminalExpression):实现文法规则的解释操作,通常包含对其他表达式的引用
上下文 (Context):包含解释器之外的全局信息
客户端 (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. 解释器模式的优点
易于扩展文法:添加新的表达式类即可扩展语言
实现文法简单:类与文法规则一一对应,易于实现
易于改变解释方式:通过修改解释器类来改变解释行为
适合领域特定语言(DSL):特别适合实现简单的领域特定语言
5. 适用场景
需要解释执行一种语言的场景
文法相对简单的场景(复杂文法可能难以维护)
效率不是关键因素的场景(解释器模式通常效率不高)
特定领域语言(DSL)的实现
规则引擎的实现
SQL解析、正则表达式等
6. 实际应用案例
正则表达式引擎:解释和匹配正则表达式
SQL解析器:解析和执行SQL语句
规则引擎:解释和执行业务规则
编译器前端:语法分析和语义分析
计算器应用:解析和计算数学表达式
配置文件解析:解释特定格式的配置文件
7. 注意事项
复杂度限制:解释器模式适合简单的文法,复杂文法会导致类数量爆炸
性能考虑:解释执行通常比直接执行代码慢
可维护性:每个规则对应一个类,规则多时代码量大
扩展性:添加新规则需要修改现有代码,可能违反开闭原则
解释器模式为特定领域的问题提供了一种优雅的解决方案,但在使用时需要权衡其优缺点,确保它适合你的特定需求。