本筆記摘抄自:https://www.cnblogs.com/PatrickLiu/p/8242238.html,記錄一下學習程序以備后續查用,
一、引言
今天我們要講行為型設計模式的第十一個模式--解釋器模式,也是面向物件設計模式的最后一個模式,先要說明一下,其實這個模式不是最后一個模
式(按Gof的排序來講),為什么把它放在最后呢?因為我們在業務系統中寫一個解釋器的機會并不是很多,實踐比較少,理解和應用該模式就有些困
難,所以就放在最后來說,先從名稱上來看這個模式,“解釋器”和Google的中英翻譯功能類似,假設有一天你去國外比如美國旅游,如果不會講英語也
聽不懂,溝通就是一個大障礙,估計也會玩得不盡興了,因為有很多景點的解說你可能不明白(沒有中文翻譯的情況下,一般情況會有的),所以我們
需要一個翻譯軟體(如科大訊飛),可以把中英文互譯,那彼此就可以更好的理解對方的意思,個人覺得這個翻譯軟體也可以稱得上是解釋器,把你不
懂的解釋成你能理解的,我們寫代碼,需要編譯器把我們寫的代碼編譯成機器可以理解的機器語言,從這方面來講,C#的編譯器也是一種解釋器,
二、解釋器模式介紹
解釋器模式:英文名稱--Interpreter Pattern;分類--行為型,
2.1、動機(Motivate)
在軟體構建程序中,如果某一特定領域的問題比較復雜,類似的模式不斷重復出現,如果使用普通的編程方式來實作將面臨非常頻繁的變化,在這種
情況下,將特定領域的問題表達為某種語法規則下的句子,然后構建一個解釋器來解釋這樣的句子,從而達到解決問題的目的,
2.2、意圖(Intent)
給定一個語言,定義它的文法的一種表示,并定義一種解釋器,這個解釋器使用該表示來解釋語言中的句子,——《設計模式》GoF
2.3、結構圖(Structure)

2.4、模式的組成
可以看出,在解釋器模式的結構圖有以下角色:
1)抽象運算式(AbstractExpression):定義解釋器的介面,約定解釋器的解釋操作,其中的Interpret介面,正如其名字那樣,它是專門用來解釋該解
釋器所要實作的功能,
2)終結符運算式(Terminal Expression):實作了抽象運算式角色所要求的介面,主要是一個interpret()方法,文法中的每一個終結符都有一個具體終
結運算式與之相對應,比如有一個簡單的公式R=R1+R2,在里面R1和R2就是終結符,對應決議R1和R2的解釋器就是終結符運算式,
3)非終結符運算式(Nonterminal Expression):文法中的每一條規則都需要一個具體的非終結符運算式,非終結符運算式一般是文法中的運算子或者
其他關鍵字,比如公式R=R1+R2中,“+”就是非終結符,決議“+”的解釋器就是一個非終結符運算式,
4)環境角色(Context):這個角色的任務一般是用來存放文法中各個終結符所對應的具體值,比如R=R1+R2,我們給R1賦值100,給R2賦值200,這
些資訊需要存放到環境角色中,很多情況下我們使用Map來充當環境角色就足夠了,
5)客戶端(Client):指的是使用解釋器的客戶端,通常在這里將按照語言的語法做的運算式轉換成使用解釋器物件描述的抽象語法樹,然后呼叫解
釋操作,
2.5、解釋器模式的具體實作
在很多場合都需要把數字轉換成中文,我們就可以使用解釋器來實作該功能,把給定的數字解釋成符合語法規范的漢字表示法,實作代碼如下:
class Program { /// <summary> /// 環境背景關系 /// </summary> public sealed class Context { public string Statement { get; set; } public int Data { get; set; } public Context(string statement) { Statement = statement; } } /// <summary> /// 抽象運算式 /// </summary> public abstract class Expression { protected Dictionary<string, int> table = new Dictionary<string, int>(9); protected Expression() { table.Add("一", 1); table.Add("二", 2); table.Add("三", 3); table.Add("四", 4); table.Add("五", 5); table.Add("六", 6); table.Add("七", 7); table.Add("八", 8); table.Add("九", 9); } public virtual void Interpreter(Context context) { if (context.Statement.Length == 0) { return; } foreach (string key in table.Keys) { int value =https://www.cnblogs.com/atomy/p/ table[key]; if (context.Statement.EndsWith(key + GetPostFix())) { context.Data += value * Multiplier(); context.Statement = context.Statement.Substring(0, context.Statement.Length - GetLength()); } if (context.Statement.EndsWith("零")) { context.Statement = context.Statement.Substring(0, context.Statement.Length - 1); } } } public abstract string GetPostFix(); public abstract int Multiplier(); //十、百、千位數使用,所以用虛方法, public virtual int GetLength() { return GetPostFix().Length + 1; } } /// <summary> /// 個位運算式 /// </summary> public sealed class GeExpression : Expression { public override string GetPostFix() { return ""; } public override int Multiplier() { return 1; } public override int GetLength() { return 1; } } /// <summary> /// 十位運算式 /// </summary> public sealed class ShiExpression : Expression { public override string GetPostFix() { return "十"; } public override int Multiplier() { return 10; } } /// <summary> /// 百位運算式 /// </summary> public sealed class BaiExpression : Expression { public override string GetPostFix() { return "百"; } public override int Multiplier() { return 100; } } /// <summary> /// 千位運算式 /// </summary> public sealed class QianExpression : Expression { public override string GetPostFix() { return "千"; } public override int Multiplier() { return 1000; } } /// <summary> /// 萬位運算式 /// </summary> public sealed class WanExpression : Expression { public override string GetPostFix() { return "萬"; } public override int Multiplier() { return 10000; } public override int GetLength() { return 1; } public override void Interpreter(Context context) { if (context.Statement.Length == 0) { return; } ArrayList tree = new ArrayList { new GeExpression(), new ShiExpression(), new BaiExpression(), new QianExpression() }; foreach (string key in table.Keys) { if (context.Statement.EndsWith(GetPostFix())) { int temp = context.Data; context.Data = 0; context.Statement = context.Statement.Substring(0, context.Statement.Length - GetLength()); foreach (Expression exp in tree) { exp.Interpreter(context); } context.Data = temp + context.Data * Multiplier(); } } } } /// <summary> /// 億位運算式 /// </summary> public sealed class YiExpression : Expression { public override string GetPostFix() { return "億"; } public override int Multiplier() { return 100000000; } public override int GetLength() { return 1; } public override void Interpreter(Context context) { ArrayList tree = new ArrayList { new GeExpression(), new ShiExpression(), new BaiExpression(), new QianExpression() }; foreach (string key in table.Keys) { if (context.Statement.EndsWith(GetPostFix())) { int temp = context.Data; context.Data = 0; context.Statement = context.Statement.Substring(0, context.Statement.Length - GetLength()); foreach (Expression exp in tree) { exp.Interpreter(context); } context.Data = temp + context.Data * Multiplier(); } } } } static void Main(string[] args) { #region 解釋器模式 //分解:((五)億)((七千)(三百)(零)(二)萬) ((六千)(四百)(五十)(二)) string roman = "五億七千三百零二萬六千四百五十二"; Context context = new Context(roman); ArrayList tree = new ArrayList { new GeExpression(), new ShiExpression(), new BaiExpression(), new QianExpression(), new WanExpression(), new YiExpression() }; foreach (Expression exp in tree) { exp.Interpreter(context); } Console.Write(context.Data); Console.Read(); #endregion } }View Code
運行結果如下:

三、解釋器模式的實作要點
使用Interpreter模式來表示文法規則,從而可以使用面向物件技巧方便地“擴展”文法,
Interpreter模式比較適合簡單的文法表示,對于復雜的文法表示,Interpreter模式會產生比較大的類層次結構,需要求助于語法分析生成器這樣的標準
工具,
3.1、解釋器模式的主要優點
1)易于改變和擴展文法,
2)每一條文法規則都可以表示為一個類,因此可以方便地實作一個簡單的語言,
3)實作文法較為容易,在抽象語法樹中每一個運算式節點類的實作方式都是相似的,這些類的代碼撰寫都不會特別復雜,還可以通過一些工具自動生
成節點類代碼,
4)增加新的解釋運算式較為方便,如果用戶需要增加新的解釋運算式只需要對應增加一個新的終結符運算式或非終結符運算式類,原有運算式類代碼
無須修改,符合“開閉原則”,
3.2、解釋器模式的主要缺點
1)對于復雜文法難以維護,在解釋器模式中,每一條規則至少需要定義一個類,因此如果一個語言包含太多文法規則,類的個數將會急劇增加,導致
系統難以管理和維護,此時可以考慮使用語法分析程式等方式來取代解釋器模式,
2)執行效率較低,由于在解釋器模式中使用了大量的回圈和遞回呼叫,因此在解釋較為復雜的句子時其速度很慢,而且代碼的除錯程序也比較麻煩,
3.3、在下面的情況下可以考慮使用解釋器模式
Interpreter模式的應用場合是Interpreter模式應用中的難點,只有滿足“業務規則頻繁變化且類似的模式不斷重復出現,同時容易抽象為語法規則的問題”
才適合使用Interpreter模式,
1)當一個語言需要解釋執行,并可以將該語言中的句子表示為一個抽象語法樹的時候,可以考慮使用解釋器模式(如XML檔案解釋、正則運算式等領
域)
2)一些重復出現的問題可以用一種簡單的語言來進行表達,
3)一個語言的文法較為簡單,
4)當執行效率不是關鍵和主要關心的問題時可考慮解釋器模式(注:高效的解釋器通常不是通過直接解釋抽象語法樹來實作的,而是需要將它們轉換
成其他形式,使用解釋器模式的執行效率并不高,)
四、.NET中解釋器模式的實作
正則運算式就是一個典型的解釋器,ASP.NET中,把aspx檔案轉化為dll時,會對html語言進行處理,這個處理程序也包含了解釋器的模式在里面,
Interpreter模式其實有Composite模式的影子,但它們解決的問題是不一樣的,
五、總結
至此,23種設計模式都寫完了,解釋器模式可以和其他模式混合使用,具體的使用方法和解決的問題我列出了一個表,大家好好了解一下,或許對大
家有些幫助,串列如下:
(1)解釋器和組合模式
這兩種可以組合使用,一般非終結符解釋器相當于組合模式中的組合物件,終結符解釋器相當于葉子物件,
(2)解釋器模式和迭代器模式
由于解釋器模式通常使用組合模式來實作,因此在遍歷整個物件結構時,可以使用迭代器模式,
(3)解釋器模式和享元模式
在使用解釋器模式的時候,可能會造成多個細粒度物件,如各式各樣的終結符解釋器,而這些終結符解釋器對不同的運算式來說是一樣的,是可以共用
的,因此可以引入享元模式來共享這些物件,
(4)解釋器模式和訪問者模式
在解釋器模式中,語法規則和解釋器物件是有對應關系的,語法規則的變動意味著功能的變化,自然會導致使用不同的解釋器物件,而且一個語法規可
以被不同的解釋器解釋執行,因此在構建抽象語法樹的時候,如果每個節點所對應的解釋器物件是固定的,這意味著該節點對應的功能是固定的,那么就
不得不根據需要來構建不同的抽象語法樹,為了讓構建的抽象語法樹較為通用,那就要求解釋器的功能不要那么固定,要能很方便地改變解釋器的功能,
這個時候就變成了如何能夠很方便地更改樹形結構中節點物件的功能了,訪問者模式可以很好的實作這個功能,
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/68920.html
標籤:C#
上一篇:C# 最簡單的鏈式呼叫例子
