問題:
在物件導向的設計中和開發中,經常會遇到,有一些請求或操作,很難用對象的形式來表示或者處理,比如我們寫一個簡單的算術計算工具計算“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:
代碼說明:
一個解析算術運算式計算的例子,為了快速說明問題,例子僅支援雙運算元運算
/// <summary>
/// 抽象運算式(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,Regex等等。