解釋器(Interpreter)模式的定義:給分析對象定義一個語言,並定義該語言的文法表示,再設計一個解析器來解釋語言中的句子。也就是說,用編譯語言的方式來分析應用中的實例。這種模式實現了文法表達式處理的接口,該接口解釋一個特定的上下文。
這裡提到的文法和句子的概念同編譯原理中的描述相同,「文法」指語言的語法規則,而「句子」是語言集中的元素。例如,漢語中的句子有很多,「我是中國人」是其中的一個句子,可以用一棵語法樹來直觀地描述語言中的句子。
解釋器模式是一種類行為型模式,其主要優點如下:
解釋器模式的主要缺點如下:
執行效率較低:解釋器模式中通常使用大量的循環和遞歸調用,當要解釋的句子較複雜時,其運行速度很慢,且代碼的調試過程也比較麻煩。
會引起類膨脹:解釋器模式中的每條規則至少需要定義一個類,當包含的文法規則很多時,類的個數將急劇增加,導致系統難以管理與維護。
可應用的場景比較少:在軟體開發中,需要定義語言文法的應用實例非常少,所以這種模式很少被使用到。
解釋器模式常用於對簡單語言的編譯或分析實例中,為了掌握好它的結構與實現,必須先了解編譯原理中的「文法、句子、語法樹」等相關概念。
文法:文法是用於描述語言的語法結構的形式規則。沒有規矩不成方圓,任何事情都要有規則,語言也一樣,不管它是機器語言還是自然語言,都有它自己的文法規則。例如,中文中的「句子」的文法如下:
〈句子〉::=〈主語〉〈謂語〉〈賓語〉
〈主語〉::=〈代詞〉|〈名詞〉
〈謂語〉::=〈動詞〉
〈賓語〉::=〈代詞〉|〈名詞〉
〈代詞〉你|我|他
〈名詞〉7大學生I筱霞I英語
〈動詞〉::=是|學習
註:這裡的符號「::=」表示「定義為」的意思,用「〈」和「〉」括住的是非終結符,沒有括住的是終結符。
句子:句子是語言的基本單位,是語言集中的一個元素,它由終結符構成,能由「文法」推導出。例如,上述文法可以推出「我是大學生」,所以它是句子。
語法樹:語法樹是句子結構的一種樹型表示,它代表了句子的推導結果,它有利於理解句子語法結構的層次。下圖所示是「我是大學生」的語法樹:
有了以上基礎知識,現在來介紹解釋器模式的結構就簡單了。解釋器模式的結構與組合模式相似,不過其包含的組成元素比組合模式多,而且組合模式是對象結構型模式,而解釋器模式是類行為型模式。
模式的結構解釋器模式包含以下主要角色:
抽象表達式(Abstract Expression)角色:定義解釋器的接口,約定解釋器的解釋操作,主要包含解釋方法 Interpret()。
終結符表達式(Terminal Expression)角色:是抽象表達式的子類,用來實現文法中與終結符相關的操作,文法中的每一個終結符都有一個具體終結表達式與之相對應。
非終結符表達式(Nonterminal Expression)角色:也是抽象表達式的子類,用來實現文法中與非終結符相關的操作,文法中的每條規則都對應於一個非終結符表達式。
環境(Context)角色:通常包含各個解釋器需要的數據或是公共的功能,一般用來傳遞被所有解釋器共享的數據,後面的解釋器可以從這裡獲取這些值。
客戶端(Client):主要任務是將需要分析的句子或表達式轉換成使用解釋器對象描述的抽象語法樹,然後調用解釋器的解釋方法,當然也可以通過環境角色間接訪問解釋器的解釋方法。
解釋器模式的結構圖如圖所示:
模式的實現解釋器模式實現的關鍵是定義文法規則、設計終結符類與非終結符類、畫出結構圖,必要時構建語法樹,其代碼結構如下:
static void Main(string[] args){ Context context = new Context(); IList<AbstractExpression> list = new List<AbstractExpression>(); list.Add(new TerminalExpression()); list.Add(new NonterminalExpression()); list.Add(new TerminalExpression()); list.Add(new TerminalExpression());
foreach (AbstractExpression exp in list) { exp.Interpret(context); }
Console.Read();}class Context{ private string input; public string Input { get { return input; } set { input = value; } } private string output; public string Output { get { return output; } set { output = value; } }} abstract class AbstractExpression{ public abstract void Interpret(Context context);} class TerminalExpression : AbstractExpression{ public override void Interpret(Context context) { Console.WriteLine("終端解釋器"); }}class NonterminalExpression : AbstractExpression{ public override void Interpret(Context context) { Console.WriteLine("非終端解釋器"); }}程序運行結果如下:
前面介紹了解釋器模式的結構與特點,下面分析它的應用場景:
注意:解釋器模式在實際的軟體開發中使用比較少,因為它會引起效率、性能以及維護等問題。如果碰到對表達式的解釋,在 C# 中可以用 Expression類 或 Flee 等來設計。