解釋器模式的定義
定義: 給定一門語言,定義它的文法的一種表示, 并定義一個解釋器, 該解釋器使用該表示來解釋語言中的句子.
其類圖如下:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIx0DciV2dmADM30zd-cWZwpmLENTJENTJ3pFbC5WY1FFVNlXRU5EerRVTyEEVNlXQq1kdjdlYwlzUiZnTtxkeO1mYxwGWhNnRtxkb1cVYxx2ValWMpJmaxk3Y6lTbM5WOHJWaxkWW1RWbiBHcXR2b5kHT20ESjBjUIF2Lc12bj5SYphXa5VWen5WY35iclN3Ztl2Lc9CX6MHc0RHaiojIsJye.jpeg)
其中的角色說明:
- AbstractExpression 抽象解釋器: 具體的解釋任務由各個實作類完成
- TerminalExpression 終結符表達式: 實作與文法中的元素相關聯的解釋操作, 通常一個解釋器模式中隻有一個終結符表達式, 但有多個執行個體,對應不同的終結符
- NonterminalExpression 非終結符表達式: 文法中的每條規則對應于一個非終結符表達式. 非終結符表達式根據邏輯的複雜程度而增加,原則上每個文法規則都對應一個非終結符表達式
- Context 環境角色
抽象表達式代碼:
抽象表達式通常隻有一個方法, 抽象表達式是生成文法集合的關鍵, 每個文法集合完成指定文法解析任務, 它是通過遞歸調用的方式,最終由最小的文法單元進行解析完成
終結符表達式代碼:
通常,終結符表達式比較簡單,主要是處理場景元素和資料的轉換
非終結符表達式:
每個非終結符表達式都代表了一個文法規則, 并且每個文法規則都隻關心自己周邊的文法規則的結果, 是以這就産生了每個非終結符表達式調用自己周邊的非終結符表達式, 然後最終、最小的文法規則就是終結符表達式,終結符表達式的概念就是如此, 不能夠再參與比自己更小的文法運算了
場景類代碼:
通常Client是一個封裝類, 封裝的結果就是傳遞進來一個規範文法檔案,解析器分析後産生結果并傳回,避免了調用者與文法解析器的耦合關系
解釋器模式的應用
解釋器模式的優點:
解釋器是一個簡單文法分析工具,它最顯著的優點就是擴充性,修改文法規則隻要修改相應的非終結符表達式就可以了, 若擴充文法, 則隻要增加非終結符就可以了
解釋器模式的缺點:
- 解釋器模式會引起類膨脹. 每個文法都要産生一個非終結符表達式,文法規則比較複雜時,就可能産生大量的類檔案, 為維護帶來了非常多的麻煩
- 解釋器模式采用遞歸調用方法. 每個非終結符表達式之關心與自己有關的表達式,每個表達式需要知道最終的結果, 必須一層一層的剝繭,無論是面向對象的語言還是面向過程的語言,遞歸都是在必要條件下使用的, 它導緻調試非常複雜.
- 效率問題. 解釋器模式由于使用了大量的循環和遞歸,效率是一個不容忽視的問題,特别是一用于解析複雜、冗長的文法時,效率是難以忍受的
解釋器模式使用的場景:
- 重複發生的問題可以使用解釋器模式. 例如, 多個應用伺服器,每天産生大量的日志,需要對日志檔案進行分析處理,由于各個伺服器的日志格式不同,但是資料要素是相同的,按照解釋器的說法就是終結符表達式都是相同的,但是非終結符表達式就需要制定了.
- 一個簡單文法需要解釋的場景. 為什麼是簡單?看看非中介表達式,文法規則越多,複雜度越高,而且類間還要進行遞歸調用. 想想看, 多個類之間的調用你需要什麼樣的耐心和信心去排查問題. 是以,解釋器模式一般用來解析比較标準的字元集, 例如SQL文法分析,不過該部分逐漸被專用工具所取代
盡量不要在重要的子產品中使用解釋器模式,否則維護會是一個很大的問題.在項目中可以使用shell、JRuby等腳本語言來代替解釋器模式,你不Java編譯型語言的不足.
解釋器模式在實際的系統開發中使用的非常少, 因為它會引起效率、性能以及維護等問題,一般在大中型的架構型項目中能夠找到它的身影, 如一些資料分析工具、報表設計工具、科學計算工具等, 若你确實遇到"一種特定類型的問題發生的頻率足夠高"的情況,準備使用解釋器模式時, 可以考慮一下 Expression4J、MESP、Jep等開源的解析工具包,功能都異常強大,而且非常容易使用,效率也還不錯,實作大多數的數學運算完全沒有問題.