1. 程式人生 > >解釋器模式的認知

解釋器模式的認知

完成 模式 簡單的類 簡單 機構 是不是 dexp 類文件 一個表

解釋器模式(Interpreter Pattern)提供了評估語言的語法或表達式的方式,它屬於行為型模式。這種模式實現了一個表達式接口,該接口解釋一個特定的上下文。這種模式被用在 SQL 解析、符號處理引擎等。

介紹

意圖:給定一個語言,定義它的文法表示,並定義一個解釋器,這個解釋器使用該標識來解釋語言中的句子。

主要解決:對於一些固定文法構建一個解釋句子的解釋器。

何時使用:如果一種特定類型的問題發生的頻率足夠高,那麽可能就值得將該問題的各個實例表述為一個簡單語言中的句子。這樣就可以構建一個解釋器,該解釋器通過解釋這些句子來解決該問題。

如何解決:構件語法樹,定義終結符與非終結符。

關鍵代碼:構件環境類,包含解釋器之外的一些全局信息,一般是

HashMap

應用實例:編譯器、運算表達式計算。

優點: 1、可擴展性比較好,靈活。 2、增加了新的解釋表達式的方式。 3、易於實現簡單文法。

缺點: 1、可利用場景比較少。 2、對於復雜的文法比較難維護。 3、解釋器模式會引起類膨脹。 4、解釋器模式采用遞歸調用方法。

使用場景: 1、可以將一個需要解釋執行的語言中的句子表示為一個抽象語法樹。 2、一些重復出現的問題可以用一種簡單的語言來進行表達。 3、一個簡單語法需要解釋的場景。

解釋器模式的應用

解釋器模式的優點

解釋器是一個簡單語法分析工具,它最顯著的優點就是擴展性,修改語法規則只要修改相應的非終結符表達式就可以了,若擴展語法,則只要增加非終結符類就可以了。

解釋器模式的缺點

解釋器模式會引起類膨脹

每個語法都要產生一個非終結符表達式,語法規則比較復雜時,就可能產生大量的類文件,為維護帶來了非常多的麻煩。

解釋器模式采用遞歸調用方法

每個非終結符表達式只關心與自己有關的表達式,每個表達式需要知道最終的結果,必須一層一層地剝繭,無論是面向過程的語言還是面向對象的語言,遞歸都是在必要條件下使用的,它導致調試非常復雜。想想看,如果要排查一個語法錯誤,我們是不是要一個一個斷點的調試下去,直到最小的語法單元。

效率問題。解釋器模式由於使用了大量的循環和遞歸,效率是個不容忽視的問題,特別是用於解析復雜、冗長的語法時,效率是難以忍受的。

解釋器模式使用的場景

重復發生的問題可以使用解釋器模式

例如,多個應用服務器,每天產生大量的日誌,需要對日誌文件進行分析處理,由於各個服務器的日誌格式不同,但是數據要素是相同的,按照解釋器的說法就是終結符表達式都是相同的,但是非終結符表達式就需要制定了。在這種情況下,可以通過程序來一勞永逸地解決該問題。

一個簡單語法需要解釋的場景

  為什麽是簡單?看看非終結表達式,文法規則越多,復雜度越高,而且類間還要進行遞歸調用(看看我們例子中的堆棧),不是一般地復雜。想想看,多個類之間的調用你需要什麽樣的耐心和信心去排查問題。因此,解釋器模式一般用來解析比較標準的字符集,例如SQL語法分析,不過該部分逐漸被專用工具所取代。

  在某些特用的商業環境下也會采用解釋器模式,我們剛剛的例子就是一個商業環境,而且現在模型運算的例子非常多,目前很多商業機構已經能夠提供出大量的數據進行分析。

解釋器模式的註意事項

盡量不要在重要的模塊中使用解釋器模式,否則維護會是一個很大的問題。在項目中可以使用shellJRubyGroovy等腳本語言來代替解釋器模式,彌補Java編譯型語言的不足。我們在一個銀行的分析型項目中就采用JRuby進行運算處理,避免使用解釋器模式的四則運算,效率和性能各方面表現良好。

最佳實踐

解釋器模式在實際的系統開發中使用的非常少,因為它會引起效率、性能以及維護等問題,一般在大中型的框架型項目能夠找到它的身影,比如一些數據分析工具、報表設計工具、科學計算工具等等

通用類圖

技術分享圖片

實例

輸入一個模型公式(加減四則運算),然後輸入模型中的參數,運算出結果。

需求不復雜,若僅僅對數字采用四則運算,每個程序員都可以寫出來。但是增加了增加模型公式就復雜了。先解釋一下為什麽需要公式, 而不采用直接計算的方法,例如有如下3個公式:

業務種類1的公式:a+b+c-d

業務種類2的公式:a+b+e-d

業務種類3的公式:a-f

我們來想,公式中有什麽?僅有兩類元素:運算元素和運算符號,運算元素就是指abc等符號,需要具體賦值的對象,也叫做終結符號,為什麽叫終結符號呢?因為這些元素除了需要賦值外,不需要做任何處理,所有運算元素都對應一個具體的業務參數,這是語法中最小的單元邏輯,不可再拆分;運算符號就是加減符號,需要我們編寫算法進行處理,每個運算符號都要對應處理單元,否則公式無法運行,運算符號也叫做非終結符號。兩類元素的共同點是都要被解析,不同點是所有的運算元素具有相同的功能,可以用一個類表示,而運算符號則是需要分別進行解釋,加法需要加法解析器,減法也需要減法解析器。分析到這裏,我們就可以先畫一個簡單的類圖

技術分享圖片

很簡單的一個類圖,VarExpression用來解析運算元素,各個公式能運算元素的數量是不同的,但每個運算元素都對應一個VarExpression對象。SybmolExpression負責運算符號解析,由兩個子類AddExpression(負責加法運算)和SubExpression(負責減法運算)來實現。解析的工作完成了,我們還需要把安排運行的先後順序(加減法是不用考慮,但是乘除法呢?註意擴展性),並且還要返回結果,因此我們需要增加一個封裝類來進行封裝處理,由於我們只做運算,暫時還不與業務有關聯,定義為Calculator類,分析到這裏,思路就比較清晰了

技術分享圖片

每個運算符號都只和自己左右兩個數字有關系,但左右兩個數字有可能也是一個解析的結果,無論何種類型,都是Expression的實現類,於是在對運算符解析的子類中增加了一個構造函數,傳遞左右兩個表達式。

首先,要求輸入公式。

請輸入表達式:a+b-c

其次,要求輸入公式中的參數。

請輸入a的值:100

請輸入b的值:20

請輸入c的值:40

最後,運行出結果。

運算結果為:a+b-c=80

要求輸入一個公式,然後輸入參數,運行結果出來了!那我們是不是可以修改公式?當然可以了,我們只要輸入公式,然後輸入相應的值就可以了,公式是在運行期定義的,而不是在運行前就制定好的,是不是類似於初中學過的“代數”這門課?先公式,然後賦值,運算出結果。

解釋器模式的認知