解釋器模式(Interpreter): 從名稱上來看看這個模式,個人的最初理解“解釋器”和Google的中英翻譯功能類似。如果有一天你去國外旅游去了,比如去美國吧,美國人是講英語的,我們是講漢語的,如果英語聽不懂,講不好,估計溝通就完蛋了,不能溝通,估計玩的就很難盡興了,因為有很多景點的解說你可能不 ...
解釋器模式(Interpreter):
從名稱上來看看這個模式,個人的最初理解“解釋器”和Google的中英翻譯功能類似。如果有一天你去國外旅游去了,比如去美國吧,美國人是講英語的,我們是講漢語的,如果英語聽不懂,講不好,估計溝通就完蛋了,不能溝通,估計玩的就很難盡興了,因為有很多景點的解說你可能不明白(沒有中文翻譯的情況下,一般情況會有的)。所以我們需要一個軟體,可以把中英文互譯,那彼此就可以更好的理解對方的意思,我感覺翻譯軟體也可以稱得上是解釋器,把你不懂的解釋成你能理解的。我們寫代碼,需要編譯器把我們寫的代碼編譯成機器可以理解的機器語言,從這方面來講,C#的編譯器也是一種解釋器。
解釋器模式的角色:
1)抽象解釋器(AbstractExpression):定義解釋器的介面,約定解釋器的解釋操作。其中的Interpret介面,正如其名字那樣,它是專門用來解釋該解釋器所要實現的功能。
2)終結符表達式(TermialExpression):實現了抽象表達式角色所要求的介面,主要是一個interpret()方法;文法中的每一個終結符都有一個具體終結表達式與之相對應。比如有一個簡單的公式R=R1+R2,在裡面R1和R2就是終結符,對應的解析R1和R2的解釋器就是終結符表達式。
3)非終結符表達式(NonterminalExpression):文法中的每一條規則都需要一個具體的非終結符表達式,非終結符表達式一般是文法中的運算符或者其他關鍵字,比如公式R=R1+R2中,“+”就是非終結符,解析“+”的解釋器就是一個非終結符表達式。
4)環境角色(Context):這個角色的任務一般是用來存放文法中各個終結符所對應的具體值,比如R=R1+R2,我們給R1賦值100,給R2賦值200。這些信息需要存放到環境角色中,很多情況下我們使用Map來充當環境角色就足夠了。
我們演示一個判斷且或的例子。
1 public abstract class AbstractExpression 2 { 3 public abstract bool Interpret(string context); 4 } 5 6 public class TerminalExpression : AbstractExpression 7 { 8 private string data; 9 10 public TerminalExpression(string data) 11 { 12 this.data = data; 13 } 14 15 public override bool Interpret(string context) 16 { 17 return data.Contains(context); 18 } 19 } 20 21 public class OrExpression : AbstractExpression 22 { 23 private AbstractExpression expr1 = null; 24 private AbstractExpression expr2 = null; 25 26 public OrExpression(AbstractExpression expr1, AbstractExpression expr2) 27 { 28 this.expr1 = expr1; 29 this.expr2 = expr2; 30 } 31 32 public override bool Interpret(string context) 33 { 34 return expr1.Interpret(context) || expr2.Interpret(context); 35 } 36 } 37 38 public class AndExpression : AbstractExpression 39 { 40 private AbstractExpression expr1 = null; 41 private AbstractExpression expr2 = null; 42 43 public AndExpression(AbstractExpression expr1, AbstractExpression expr2) 44 { 45 this.expr1 = expr1; 46 this.expr2 = expr2; 47 } 48 49 public override bool Interpret(string context) 50 { 51 return expr1.Interpret(context) || expr2.Interpret(context); 52 } 53 } 54 55 public class Program 56 { 57 //規則:Robert 和 John 是男性 58 public static AbstractExpression GetMaleExpression() 59 { 60 AbstractExpression robert = new TerminalExpression("Robert"); 61 AbstractExpression john = new TerminalExpression("John"); 62 return new OrExpression(robert, john); 63 } 64 65 //規則:Julie 是一個已婚的女性 66 public static AbstractExpression GetMarriedWomanExpression() 67 { 68 AbstractExpression julie = new TerminalExpression("Julie"); 69 AbstractExpression married = new TerminalExpression("Married"); 70 return new AndExpression(julie, married); 71 } 72 73 public static void Main(string[] args) 74 { 75 AbstractExpression isMale = GetMaleExpression(); 76 AbstractExpression isMarriedWoman = GetMarriedWomanExpression(); 77 78 Console.WriteLine($"John is male? {isMale.Interpret("John")}"); 79 Console.WriteLine($"Julie is a married women? {isMarriedWoman.Interpret("Married Julie")}"); 80 } 81 }
這裡我們可以得出:解釋器模式有很好的擴展模式,或此時我們希望能夠找到一位男士已婚,我們只需要再寫一個非終結符表達式即可,易於擴展。我們再來看下麵這個例子。
1 // 抽象表達式 2 public abstract class Expression 3 { 4 protected Dictionary<string, int> table = new Dictionary<string, int>(9); 5 6 protected Expression() 7 { 8 table.Add("一", 1); 9 table.Add("二", 2); 10 table.Add("三", 3); 11 table.Add("四", 4); 12 table.Add("五", 5); 13 table.Add("六", 6); 14 table.Add("七", 7); 15 table.Add("八", 8); 16 table.Add("九", 9); 17 } 18 19 public virtual void Interpreter(Context context) 20 { 21 if (context.Statement.Length == 0) 22 { 23 return; 24 } 25 26 foreach (string key in table.Keys) 27 { 28 int value = table[key]; 29 30 if (context.Statement.EndsWith(key + GetPostFix())) 31 { 32 context.Data += value * this.Multiplier(); 33 context.Statement = context.Statement.Substring(0, context.Statement.Length - this.GetLength()); 34 } 35 if (context.Statement.EndsWith("零")) 36 { 37 context.Statement = context.Statement.Substring(0, context.Statement.Length - 1); 38 } 39 } 40 } 41 42 public abstract string GetPostFix(); 43 44 public abstract int Multiplier(); 45 46 //這個可以通用,但是對於個位數字例外,所以用虛方法 47 public virtual int GetLength() 48 { 49 return this.GetPostFix().Length + 1; 50 } 51 } 52 53 //個位表達式 54 public sealed class GeExpression : Expression 55 { 56 public override string GetPostFix() 57 { 58 return ""; 59 } 60 61 public override int Multiplier() 62 { 63 return 1; 64 } 65 66 public override int GetLength() 67 { 68 return 1; 69 } 70 } 71 72 //十位表達式 73 public sealed class ShiExpression : Expression 74 { 75 public override string GetPostFix() 76 { 77 return "十"; 78 } 79 80 public override int Multiplier() 81 { 82 return 10; 83 } 84 } 85 86 //百位表達式 87 public sealed class BaiExpression : Expression 88 { 89 public override string GetPostFix() 90 { 91 return "百"; 92 } 93 94 public override int Multiplier() 95 { 96 return 100; 97 } 98 } 99 100 //千位表達式 101 public sealed class QianExpression : Expression 102 { 103 public override string GetPostFix() 104 { 105 return "千"; 106 } 107 108 public override int Multiplier() 109 { 110 return 1000; 111 } 112 } 113 114 //萬位表達式 115 public sealed class WanExpression : Expression 116 { 117 public override string GetPostFix() 118 { 119 return "萬"; 120 } 121 122 public override int Multiplier() 123 { 124 return 10000; 125 } 126 127 public override void Interpreter(Context context) 128 { 129 if (context.Statement.Length == 0) 130 { 131 return; 132 } 133 134 ArrayList tree = new ArrayList(); 135 136 tree.Add(new GeExpression()); 137 tree.Add(new ShiExpression()); 138 tree.Add(new BaiExpression()); 139 tree.Add(new QianExpression()); 140 141 foreach (string key in table.Keys) 142 { 143 if (context.Statement.EndsWith(GetPostFix())) 144 { 145 int temp = context.Data; 146 context.Data = 0; 147 148 context.Statement = context.Statement.Substring(0, context.Statement.Length - this.GetLength()); 149 150 foreach (Expression exp in tree) 151 { 152 exp.Interpreter(context); 153 } 154 context.Data = temp + context.Data * this.Multiplier(); 155 } 156 } 157 } 158 } 159 160 //億位表達式 161 public sealed class YiExpression : Expression 162 { 163 public override string GetPostFix() 164 { 165 return "億"; 166 } 167 168 public override int Multiplier() 169 { 170 return 100000000; 171 } 172 173 public override void Interpreter(Context context) 174 { 175 ArrayList tree = new ArrayList(); 176 177 tree.Add(new GeExpression()); 178 tree.Add(new ShiExpression()); 179 tree.Add(new BaiExpression()); 180 tree.Add(new QianExpression()); 181 182 foreach (string key in table.Keys) 183 { 184 if (context.Statement.EndsWith(GetPostFix())) 185 { 186 int temp = context.Data; 187 context.Data = 0; 188 context.Statement = context.Statement.Substring(0, context.Statement.Length - this.GetLength()); 189 190 foreach (Expression exp in tree) 191 { 192 exp.Interpreter(context); 193 } 194 context.Data = temp + context.Data * this.Multiplier(); 195 } 196 } 197 } 198 } 199 200 //環境上下文 201 public sealed class Context 202 { 203 private string _statement; 204 private int _data; 205 206 public Context(string statement) 207 { 208 this._statement = statement; 209 } 210 211 public string Statement 212 { 213 get { return this._statement; } 214 set { this._statement = value; } 215 } 216 217 public int Data 218 { 219 get { return this._data; } 220 set { this._data = value; } 221 } 222 } 223 224 internal class Program 225 { 226 private static void Main(string[] args) 227 { 228 string roman = "五億七千三百零二萬六千四百五十二"; 229 //分解:((五)億)((七千)(三百)(零)(二)萬) 230 //((六千)(四百)(五十)(二)) 231 232 Context context = new Context(roman); 233 List<Expression> tree = new List<Expression>(); 234 tree.Add(new GeExpression()); 235 tree.Add(new ShiExpression()); 236 tree.Add(new BaiExpression()); 237 tree.Add(new QianExpression()); 238 tree.Add(new WanExpression()); 239 tree.Add(new YiExpression()); 240 241 foreach (Expression exp in tree) 242 { 243 exp.Interpreter(context); 244 } 245 246 Console.Write(context.Data); 247 } 248 }
看完之後是不是想罵一句fuck,我只是想要簡單的轉換一下,卻需要寫這麼一大坨,顯然不符合我們的心意。
解釋器模式的優缺點:
優點:
1)易於改變和擴展文法。
2)每一條文法規則都可以表示為一個類,因此可以方便地實現一個簡單的語言。
3)實現文法較為容易。在抽象語法樹中每一個表達式節點類的實現方式都是相似的,這些類的代碼編寫都不會特別複雜,還可以通過一些工具自動生成節點類代碼。
4)增加新的解釋表達式較為方便。如果用戶需要增加新的解釋表達式只需要對應增加一個新的終結符表達式或非終結符表達式類,原有表達式類代碼無須修改,符合“開閉原則”。
缺點:
1)對於複雜文法難以維護。在解釋器模式中,每一條規則至少需要定義一個類,因此如果一個語言包含太多文法規則,類的個數將會急劇增加,導致系統難以管理和維護,此時可以考慮使用語法分析程式等方式來取代解釋器模式。
2)執行效率較低。由於在解釋器模式中使用了大量的迴圈和遞歸調用,因此在解釋較為複雜的句子時其速度很慢,而且代碼的調試過程也比較麻煩。
解釋器模式的應用場景:
1)當一個語言需要解釋執行,並可以將該語言中的句子表示為一個抽象語法樹的時候,可以考慮使用解釋器模式(如XML文檔解釋、正則表達式等領域)。
2)一些重覆出現的問題可以用一種簡單的語言來進行表達。
3)一個語言的文法較為簡單.
4)當執行效率不是關鍵和主要關心的問題時可考慮解釋器模式(註:高效的解釋器通常不是通過直接解釋抽象語法樹來實現的,而是需要將它們轉換成其他形式,使用解釋器模式的執行效率並不高。)
參考:https://www.cnblogs.com/PatrickLiu/p/8242238.html