《設計模式之禪》——解釋器模式

        定義:Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language。(給定一個語言, 定義它的文法的一種表示,並定義一個解釋器,該解釋器使用該表示來解釋語言中的句子。)

        解釋器模式的通用類圖如圖所示。


  • Expression抽象解釋器:具體的解釋任務由各個實現類完成,具體的解釋器分別由TerminalExpression和NonterminalExpression完成。
  • TerminalExpression終結符表達式:  實現與文法中的元素相關聯的解釋操作,通常一個解釋器模式中只有一個終結符表達式,但有多個實例,對應不同的終結符。具體到我們例子就是VarExpression類,表達式中的每個終結符都在堆棧中產生了一個VarExpression對象。
  • NonterminalExpression非終結符表達式:文法中的每條規則對應於一個非終結表達式,具體到我們的例子就是加減法規則分別對應到AddExpression和SubExpression兩個類。非終結符表達式根據邏輯的複雜程度而增加,原則上每個文法規則都對應一個非終結符表達式。
  • Context 環境角色:具體到我們的例子中是採用HashMap代替。


1.解釋器模式的應用


解釋器模式的優點


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


解釋器模式的缺點

  • 解釋器模式會引起類膨脹:每個語法都要產生一個非終結符表達式,語法規則比較複雜時,就可能產生大量的類文件,爲維護帶來了非常多的麻煩。
  • 解釋器模式採用遞歸調用方法:每個非終結符表達式只關心與自己有關的表達式,每個表達式需要知道最終的結果,必須一層一層地剝繭,無論是面向過程的語言還是面向對象的語言,遞歸都是在必要條件下使用的,它導致調試非常複雜。想想看,如果要排查一個語法錯誤,我們是不是要一個一個斷點的調試下去,直到最小的語法單元。
  • 效率問題:解釋器模式由於使用了大量的循環和遞歸,效率是個不容忽視的問題,特別是用於解析複雜、冗長的語法時,效率是難以忍受的。


解釋器模式使用的場景


  • 重複發生的問題可以使用解釋器模式:例如,多個應用服務器,每天產生大量的日誌,需要對日誌文件進行分析處理,由於各個服務器的日誌格式不同,但是數據要素是相同的,按照解釋器的說法就是終結符表達式都是相同的,但是非終結符表達式就需要制定了。在這種情況下,可以通過程序來一勞永逸地解決該問題。
  • 一個簡單語法需要解釋的場景:爲什麼是簡單?看看非終結表達式,文法規則越多,複雜度越高,而且類間還要進行遞歸調用(看看我們例子中的堆棧),不是一般地複雜。想想看,多個類之間的調用你需要什麼樣的耐心和信心去排查問題。因此,解釋器模式一般用來解析比較標準的字符集,例如SQL語法分析,不過該部分逐漸被專用工具所取代。在某些特用的商業環境下也會採用解釋器模式,我們剛剛的例子就是一個商業環境,而且現在模型運算的例子非常多,目前很多商業機構已經能夠提供出大量的數據進行分析。 


解釋器模式的注意事項


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


2.最佳實踐


       解釋器模式在實際的系統開發中使用的非常少,因爲它會引起效率、性能以及維護等問題,一般在大中型的框架型項目能夠找到它的身影,比如一些數據分析工具、報表設計工具、科學計算工具等等,若你確實遇到“一種特定類型的問題發生的頻率足夠高”的情況,準備使用解釋器模式時,可以考慮一下Expression4J、MESP(Math Expression String Parser)、Jep等開源的解析工具包(這三個開源產品都可以百度、Google中搜索到,請讀者自行查詢),功能都異常強大,而且非常容易使用,效率也還不錯,實現大多數的數學運算完全沒有問題,自己沒有必要從頭開始編寫解釋器,有人已經建立了一條康莊大道,何必再走自己的泥濘小路呢?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章