設計模式-行為型-解釋器模式

  • 2019 年 10 月 11 日
  • 筆記

解釋器模式(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