二十三種設計模式[15] - 直譯器模式(Interpreter Pattern)
前言
直譯器模式,類行為型模式。一種用來解釋特定文法(語言的語法和表示式)規則的方式。這種行為模式使用了類似組合的結構來構建一個抽象語法樹(Abstract Syntax Tree,AST),用來描述該直譯器所解釋的語法。如果你想要了解組合模式,可跳轉至二十三種設計模式[8] - 組合模式(Composite Pattern)。
“ 給定一個語言,定義它的文法的一種表示,並定義一個直譯器,這個直譯器使用該表示來解釋語言中的句子。 ” ——《設計模式 - 可複用的面向物件軟體》
抽象語法樹(Abstract Syntax Tree,AST)
抽象語法樹是一種語法或表示式結構的樹狀表現形式。比如“ 1+2+3+4+5 ”的樹狀表現形式如下。
結構
- Client(呼叫者):直譯器的呼叫者;
- Context(上下文):用來儲存直譯器的全域性資訊;
- AbstractExpression(抽象表示式):語法樹中所有節點的共有父類,用來定義解釋操作和保持所有節點的一致性;
- TerminalExpression(終結符表示式):語法樹中的葉節點,實現文法中的終結符的解釋操作。文法中每一個終結符對應一個終結符表示式類;
- NontermainalExpression(非終結符表示式):語法樹中的根節點,實現文法中的非終結符的解釋操作。該解釋操作一般要遞迴呼叫其子節點的解釋操作;
注:在“ 1+2+3+4+5 ”中,1、2、3、4、5為終結符,符號+為非終結符。
示例
還是以加、減、乘、除運算為例,下面將實現一個根據字串進行求和運算的Demo。
/// <summary> /// 抽象表示式介面 /// </summary> public interface IExpression { double Interpret(); } /// <summary> /// 終結符表示式 /// </summary> public class ValueExpression : IExpression { private string Value { set; get; } = string.Empty; public ValueExpression(string value) { this.Value = value; } public double Interpret() { return Convert.ToDouble(this.Value); } } /// <summary> /// 非終結符表示式,加法 /// </summary> public class PlusExpression : IExpression { private List<IExpression> ExpressionList { set; get; } = new List<IExpression>(); public PlusExpression(List<IExpression> expressions) { this.ExpressionList = expressions; } public double Interpret() { double sum = this.ExpressionList.FirstOrDefault().Interpret(); for (int i = 1, len = this.ExpressionList.Count; i < len; i++) { sum += this.ExpressionList[i].Interpret(); } return sum; } } /// <summary> /// 非終結符表示式,減法 /// </summary> public class MinusExpression : IExpression { private List<IExpression> ExpressionList { set; get; } = new List<IExpression>(); public MinusExpression(List<IExpression> expressions) { this.ExpressionList = expressions; } public double Interpret() { double sum = this.ExpressionList.FirstOrDefault().Interpret(); for (int i = 1, len = this.ExpressionList.Count; i < len; i++) { sum -= this.ExpressionList[i].Interpret(); } return sum; } } /// <summary> /// 非終結符表示式,乘法 /// </summary> public class MultiplicationExpression : IExpression { private List<IExpression> ExpressionList { set; get; } = new List<IExpression>(); public MultiplicationExpression(List<IExpression> expressions) { this.ExpressionList = expressions; } public double Interpret() { double sum = this.ExpressionList.FirstOrDefault().Interpret(); for (int i = 1, len = this.ExpressionList.Count; i < len; i++) { sum *= this.ExpressionList[i].Interpret(); } return sum; } } /// <summary> /// 非終結符表示式,除法 /// </summary> public class DivisionExpression : IExpression { private List<IExpression> ExpressionList { set; get; } = new List<IExpression>(); public DivisionExpression(List<IExpression> expressions) { this.ExpressionList = expressions; } public double Interpret() { double sum = this.ExpressionList.FirstOrDefault().Interpret(); for (int i = 1, len = this.ExpressionList.Count; i < len; i++) { sum /= this.ExpressionList[i].Interpret(); } return sum; } } /// <summary> /// 運算直譯器 /// </summary> public class OperationInterpreter { public double Interpret(string str) { if (string.IsNullOrWhiteSpace(str)) { throw new Exception("字串為空"); } double d; if (!double.TryParse(str.Replace("+", string.Empty) .Replace("-", string.Empty) .Replace("*", string.Empty) .Replace("/", string.Empty) .Replace(".", string.Empty), out d)) { throw new Exception("當前字串不可進行求和運算"); } str = str.Replace(" ", string.Empty); var plusList = str.Split(new string[] { "+" }, StringSplitOptions.RemoveEmptyEntries); ValueExpression operationValue = null; foreach (var plusItem in plusList) { ValueExpression minuValue = null; var minusList = plusItem.Split(new string[] { "-" }, StringSplitOptions.RemoveEmptyEntries); foreach (var minuItem in minusList) { ValueExpression mulValue = null; var mulList = minuItem.Split(new string[] { "*" }, StringSplitOptions.RemoveEmptyEntries); foreach (var mulItem in mulList) { var divList = mulItem.Split(new string[] { "/" }, StringSplitOptions.RemoveEmptyEntries); ValueExpression divValue = null; foreach (var divItem in divList) { if (divValue == null) { divValue = new ValueExpression(divItem); } else { var divExp = new DivisionExpression(new List<IExpression> { divValue, new ValueExpression(divItem) }); divValue = new ValueExpression(divExp.Interpret().ToString()); } } if (mulValue == null) { mulValue = divValue; } else { var mulExp = new MultiplicationExpression(new List<IExpression> { mulValue, divValue }); mulValue = new ValueExpression(mulExp.Interpret().ToString()); } } if (minuValue == null) { minuValue = mulValue; } else { var minExp = new MinusExpression(new List<IExpression> { minuValue, mulValue }); minuValue = new ValueExpression(minExp.Interpret().ToString()); } } if (operationValue == null) { operationValue = minuValue; } else { var operationExp = new PlusExpression(new List<IExpression> { operationValue, minuValue }); operationValue = new ValueExpression(operationExp.Interpret().ToString()); } } return operationValue.Interpret(); } } static void Main(string[] args) { OperationInterpreter interpreter = new OperationInterpreter(); Console.WriteLine($"6/3+2*5-1+3 = {interpreter.Interpret("6/3+2*5-1+3")}"); Console.ReadKey(); }
示例中,我們為加減乘除每個運算各定義了一個非終結符表示式類,以及代表結果的終結符表達類。為了方便客戶的使用,我們建立了一個OperationInterpreter類來處理求和運算的處理邏輯。當我們需要為這個Demo擴充套件新的運算時(比如求餘),只需要增加新的非終結符表示式類並將其增加到邏輯類中即可。表示式“ 6/3+2*5-1+3 ”的抽象語法樹如下。
總結
直譯器模式為我們增加了一種新的解釋表示式的方式,它易於實現一些簡單的文法並且也比較容易擴充套件新的文法。但由於非終結符表示式需要遞迴的呼叫其終結符表示式,使得程式碼維護難度提高。同時該模式也存在類爆炸的風險。由於直譯器模式主要是用來解釋一些特有的自定義文法,所以在我們日常開發中可使用的場景較少。
以上,就是我對直譯器模式的理解,希望對你有所幫助。
示例原始碼:https://gitee.com/wxingChen/DesignPatternsPractice
系列彙總:https://www.cnblogs.com/wxingchen/p/10031592.html
本文著作權歸本人所有,如需轉載請標明本文連結(https://www.cnblogs.com/wxingchen/p/10055295.html)