1. 程式人生 > 其它 >C#設計模式學習筆記:(23)直譯器模式

C#設計模式學習筆記:(23)直譯器模式

技術標籤:C#教程c#

本筆記摘抄自:https://www.cnblogs.com/PatrickLiu/p/8242238.html,記錄一下學習過程以備後續查用。

一、引言

今天我們要講行為型設計模式的第十一個模式--直譯器模式,也是面向物件設計模式的最後一個模式。先要說明一下,其實這個模式不是最後一個模

式(按Gof的排序來講),為什麼把它放在最後呢?因為我們在業務系統中寫一個直譯器的機會並不是很多,實踐比較少,理解和應用該模式就有些困

難,所以就放在最後來說,先從名稱上來看這個模式。“直譯器”和Google的中英翻譯功能類似,假設有一天你去國外比如美國旅遊,如果不會講英語也

聽不懂,溝通就是一個大障礙,估計也會玩得不盡興了,因為有很多景點的解說你可能不明白(沒有中文翻譯的情況下,一般情況會有的)。所以我們

需要一個翻譯軟體(如科大訊飛),可以把中英文互譯,那彼此就可以更好的理解對方的意思。個人覺得這個翻譯軟體也可以稱得上是直譯器,把你不

懂的解釋成你能理解的。我們寫程式碼,需要編譯器把我們寫的程式碼編譯成機器可以理解的機器語言,從這方面來講,C#的編譯器也是一種直譯器。

二、直譯器模式介紹

直譯器模式:英文名稱--Interpreter Pattern;分類--行為型。

2.1、動機(Motivate)

在軟體構建過程中,如果某一特定領域的問題比較複雜,類似的模式不斷重複出現,如果使用普通的程式設計方式來實現將面臨非常頻繁的變化。在這種

情況下,將特定領域的問題表達為某種語法規則下的句子,然後構建一個直譯器來解釋這樣的句子,從而達到解決問題的目的。

2.2、意圖(Intent)

給定一個語言,定義python基礎教程
它的文法的一種表示,並定義一種直譯器,這個直譯器使用該表示來解釋語言中c#教程的句子。——《設計模式》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 = 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
    }
}

執行結果如下:

在這裡插入圖片描述

三、直譯器模式的實現要點

使用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)直譯器模式和訪問者模式

在直譯器模式中,語法規則和直譯器物件是有對應關係的。語法規則的變動意味著功能的變化,自然會導致使用不同的直譯器物件,而且一個語法規可

以被不同的直譯器解釋執行。因此在構建抽象語法樹的時候,如果每個節點所對應的直譯器物件是固定的,這意味著該節點對應的功能是固定的,那麼就

不得不根據需要來構建不同的抽象語法樹。為了讓構建的抽象語法樹較為通用,那就要求直譯器的功能不要那麼固定,要能很方便地改變直譯器的功能,

這個時候就變成了如何能夠很方便地更改樹形結構中節點物件的功能了,訪問者模式可以很好的實現這個功能。