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