前言
這次介紹另一個行為模式,解釋器模式,都說解釋器模式用的少,其實只是我們在日常的開發中用的少,但是一些開源框架中還是能見到它的影子,例如:spring的spEL表達式在解析時就用到了解釋器模式,以及mybatis在將SQL語句映射成對象時關系時、還有一些解析正則表達式和解析json等開源工具。
解釋器模式
概念介紹
解釋器模式是指給定一個使用規定格式和語法的語言,並且建立一個解釋器來解釋該語言中的句子。解釋器本身就是一種按照規定的語法進行解析的方案,但是總體來說也是一種使用頻率相對較低但學習難度較大的設計模式。
舉例
因為解釋器模式用到地方不太多,實在想不到舉什么樣的例子合適,所以就使用一個簡單的來實現一個壘加的功能的例子吧。
具體過程如下:
上下文環境類
@Getter @Setter public class Context { /** * 輸入 */ private String input; /** * 結果 */ private int output; public Context(String input){ this.input = input; } @Override public String toString() { return input + "=" + output; } }
抽象表達式類
public abstract class Expression { Context context; /** * 解釋一個給定的表達式 * @param context */ public abstract void interpret(Context context); }
壘加類
/** * 壘加1 */ public class MinusExpression extends Expression { /** * 解釋一個給定的表達式 * * @param context */ @Override public void interpret(Context context) { this.context = context; String input = context.getInput(); int in = Integer.valueOf(input); context.setOutput(in-1); } @Override public String toString() { return "--"+context.getInput()+"="+context.getOutput(); } }
壘減
/** * 壘減 */ public class PlusExpression extends Expression { /** * 解釋一個給定的表達式 * * @param context */ @Override public void interpret(Context context) { this.context = context; String input = context.getInput(); int in = Integer.valueOf(input); context.setOutput(in+1); } @Override public String toString() { return "++"+context.getInput()+"="+context.getOutput(); } }
測試,使用
public class Client { public static void main(String[] args) { Context context = new Context("50"); Expression plus = new PlusExpression(); Expression minus = new MinusExpression(); //執行壘加 plus.interpret(context); System.out.println(plus.toString()); //壘減 minus.interpret(context); System.out.println(minus.toString()); } }
運行結果
++50=51 --50=49
通過運算結果可以看出來,表達式通過解釋后的結果,++50解釋后結果是51,--50解釋后結果是49。
解釋器模式分析
解釋器模式的結構圖如下:
解釋器類圖上的各個角色說明:
Expression(抽象解釋器):定義解釋方法,具體的解釋任務由各個實現類完成,具體的解釋器分別由TerminalRxpression和NonterMinalExpression完成。抽象解釋器對應上面例子中的Expression類
TerminalExpression(終結符表達式):實現與文法中的元素相關的解釋操作,一個解釋器模式中只有一個終結符表達式,但有多個實例,對應不同的終結符。上面的代碼例子中的PlusExpression和MinusExpression都是這個角色。
NoteTerminalExpression(非終結符表達式):文法中的每條規則對應於一個非終結符表達式。非終結符表達式是根據邏輯的復雜度而增加,原則上每個文法規則都對應一個非終結符表達式。由於上面舉得例子比較簡單,所以上面的例子中是沒有這個角色的。
Context(環境角色):環境類又稱為上下文類,它用於存儲解釋器之外的一些全局信息,通常它臨時存儲了需要解釋的語句。也可以使用集合用來存儲要解釋的內容。
解釋器模式總結
解釋器模式的優點
1、易於改變和擴展文法。因為該模式使用類表示文法,所以可以使用繼承改變或擴展該文法。
2、每條文法規則都可以是一個類,所以可以很方便的實現一個簡單的語言。
3、易於實現文法的定義。在抽象語法樹中每一個表達式節點類的實現方式都是相似的,這些類的代碼編寫都不會特別復雜,還可以通過一些工具自動生成節點類代碼。
4、增加新的解釋表達式較為方便。如果用戶需要增加新的解釋表達式只需要對應增加一個新的終結符表達式或非終結符表達式類,原有表達式類代碼無須修改,符合“開閉原則”。
解釋器模式的缺點
1、對於復雜文法難以維護。在解釋器模式中,每一條規則至少需要定義一個類,因此如果一個語言包含太多文法規則,類的個數將會急劇增加,導致系統難以管理和維護,此時可以考慮使用語法分析程序等方式來取代解釋器模式。
2、執行效率較低。由於在解釋器模式中使用了大量的循環和遞歸調用,因此在解釋較為復雜的句子時其速度很慢,而且代碼的調試過程也比較麻煩。
適用場景
1、可以將一個需要解釋執行的語言中的句子表示為一個抽象語法樹。
3、一個語言的文法較為簡單。
4、執行效率不是關鍵問題。【注:高效的解釋器通常不是通過直接解釋抽象語法樹來實現的,而是需要將它們轉換成其他形式,使用解釋器模式的執行效率並不高。】
想了解更多的設計模式請查看Java設計模式學習記錄-GoF設計模式概述。