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