问题:
在面向对象的设计中和开发中,经常会遇到,有一些请求或操作,很难用对象的形式来表示或者处理,比如我们写一个简单的算术计算工具计算“a+b”,可以简单的定义一个方法,接收两个变量,做算术“+”计算返回结果,可是如果让这个方法可以实现“加减乘除”四则运算,我们又要修改方法加入一个运算符参数,但是由于需求变化,又要加入多个操作数的运算如:“a+b-c*d”,该如何处理呢?穷举方法定义所有可能出现的操作显然是不可能的,我们能否定义一种来解析算术表达式的方法,直接将算术表达式作为字符串传递给计算方法,计算方法将算术表达式解析后再计算返回结果?
定义:
Interpreter模式是一种行为模式,给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
意图:
将某一特定领域的比较复杂问题,表达为某种语法规则下的句子,然后构建一个解释器来解释这样的句子,来应对使用普通的编程方式实现面临非常的频繁变化的问题。(建立语法树,然后用语法将表达式进行解析。)
参与者:
•抽象表达式(Abstract Expression)角色:声明一个所有的具体表达式角色都需要实现的抽象接口。这个接口主要是一个interpret()方法,称做解释操作。
•终结符表达式(Terminal Expression)角色:实现了抽象表达式角色所要求的接口,主要是一个interpret()方法;文法中的每一个终结符都有一个具体终结表达式与之相对应。比如有一个简单的公式R=R1+R2,在里面R1和R2就是终结符,对应的解析R1和R2的解释器就是终结符表达式。
•非终结符表达式(Nonterminal Expression)角色:文法中的每一条规则都需要一个具体的非终结符表达式,非终结符表达式一般是文法中的运算符或者其他关键字,比如公式R=R1+R2中,“+"就是非终结符,解析“+”的解释器就是一个非终结符表达式。
•环境(Context)角色:这个角色的任务一般是用来存放文法中各个终结符所对应的具体值,比如R=R1+R2,我们给R1赋值100,给R2赋值200。这些信息需要存放到环境角色中,很多情况下我们使用Map来充当环境角色就足够了。
UML:
代码说明:
一个解析算术表达式计算的例子,为了快速说明问题,例子仅支持双操作数运算
/// 抽象表达式(Abstract Expression)角色
/// </summary>
public abstract class Expression
{
public abstract void Interpret();
}
/// <summary>
/// 具体的算术解析器表达式
/// </summary>
public class CalculatorExpression : Expression
{
public int Value;
string Expression;
public CalculatorExpression(string _expression)
{
Expression = _expression;
}
public override void Interpret()
{
string[] Numbers = Expression.Split('+','-');
if (Expression.IndexOf('+')>-1)
{
this.Value = int.Parse(Numbers[0]) + int.Parse(Numbers[1]);
}
else if (Expression.IndexOf('-') > -1)
{
this.Value = int.Parse(Numbers[0]) - int.Parse(Numbers[1]);
}
else if (Expression.IndexOf('*') > -1)
{
this.Value = int.Parse(Numbers[0]) * int.Parse(Numbers[1]);
}
else if (Expression.IndexOf('/') > -1)
{
this.Value = int.Parse(Numbers[0]) / int.Parse(Numbers[1]);
}
}
}
/// <summary>
/// 环境(Context)角色
/// </summary>
public class ContextCalculator
{
/// <summary>
/// 计算方法
/// </summary>
/// <param name="expression"></param>
/// <returns></returns>
public int Calculate(string expression)
{
CalculatorExpression ex = new CalculatorExpression(expression);
ex.Interpret();
return ex.Value;
}
}
public void InterpreterTest()
{
ContextCalculator c = new ContextCalculator();
//传入表达式计算
//无论是扩展为多操作数,还是加入括号,客户端都不会受到影响。
c.Calculate("2+1");
}
优点:
•可以很容易地改变和扩展文法,因为该模式使用类表示文法规则,你可使用继承来改变或扩展文法,实现灵活的扩展。
•容易实现文法,因为定义抽象语法树中各个节点的类的实现大体类似,这些类都易于直接编写。
缺点:
•文法中的每一条规则至少定义了一个类,对于复杂的文法表示会产生比较大的类层次结构,难以管理和维护。
•因为文句会分析成树结构,解释器需要递归访问它,所以效率会受影响。
应用场景:
如果一种特定类型的问题发生的频率足够高,那么可能就值得将该问题的各个实例表述为一个简单语言中的句子。这样就可以构建一个解释器,该解释器通过解释这些句子来解决该问题。
PS:.Net系统中提供了很多解释器,如:LINQ,正则表达式等等。