給定其中一種語言,解釋器模式能夠定義出其文本的一種表示,並同時提供一個解釋器。客戶端可使用這個解釋器來解釋這個語言中的句子。java
爲了說明解釋器模式的實現辦法,這裏給出一個最簡單的文法和對應的解釋器模式的實現,模擬Java語言中對布爾表達式進行操做和求值。
在這個語言中終結符是布爾變量,也就是常量true和false。非終結符表達式包含運算符and,or和not等布爾表達式。這個簡單的文法以下:
Expression ::= Constant | Variable | Or | And | Not
And ::= Expression 'AND' Expression
Or ::= Expression 'OR' Expression
Not ::= 'NOT' Expression
Variable ::= 任何標識符
Constant ::= 'true' | 'false'‘’express
抽象語法樹(AST)的每個節點都表明一個語句,而在每個節點上均可以執行解釋方法。這個解釋方法的執行就表明這個語句被解釋。因爲每個語句都表明一個常見的問題的實例,所以每個節點上的解釋操做都表明對一個問題實例的解答。ide
其結構以下:this
(1)抽象表達式角色(expression):聲明一個全部的具體表達式都須要實現的抽象接口。這個接口主要是一個interpret()方法,稱做解釋方法。spa
(2)終結符表達式(Terminal Expression):這是一個具體表達式。好比 A and B 中的 a、b就是一個終結符表達式code
實現了上述抽象表達式角色聲明的接口,主要是一個interpret()方法;blog
文法中的每個終結符都有一個具體終結表達式與之對應。遞歸
(3)非終結表達式(Nonterminal Expression):一個具體角色。好比 A and B 中的 and就是一個終結符表達式接口
文法中的每一條規則R=R1R2R3...Rn都須要一個具體的非終結符表達式;字符串
對每個R1R2R3...Rn中的符號都持有一個靜態類型爲Expression的實例變量;
實現解釋操做,即interpret()方法。解釋操做以遞歸方式調用上面的R1R2R3...Rn中的各個符號的實例變量。
(4)客戶端角色:構造一個抽象語法樹(AST或AbstractSyntaxTree);調用解釋操做interpret()
(5)環境角色(Context):提供解釋器以外的一些全局信息,好比變量的真實量值等。
源碼以下:
抽象表達式角色:
package expression; public abstract class Expression { /** * 以環境爲準,本方法解釋給定的任何一個表達式 */ public abstract boolean interpret(Context ctx); /** * 檢驗兩個表達式在結構上是否相同 */ public abstract boolean equals(Object obj); /** * 返回表達式的hash code */ public abstract int hashCode(); /** * 將表達式轉換成字符串 */ public abstract String toString(); }
Constant常量表明一個布爾常量:
package expression; public class Constant extends Expression { private boolean value; public Constant(boolean value) { this.value = value; } @Override public boolean equals(Object obj) { if (obj != null && obj instanceof Constant) { return this.value == ((Constant) obj).value; } return false; } @Override public int hashCode() { return this.toString().hashCode(); } @Override public boolean interpret(Context ctx) { return value; } @Override public String toString() { return new Boolean(value).toString(); } }
Variable表明有名變量,在使用Variable類時,須要將變量名傳入構造中:
package expression; public class Variable extends Expression { private String name; public Variable(String name) { this.name = name; } @Override public boolean equals(Object obj) { if (obj != null && obj instanceof Variable) { return this.name.equals(((Variable) obj).name); } return false; } @Override public int hashCode() { return this.toString().hashCode(); } @Override public String toString() { return name; } @Override public boolean interpret(Context ctx) { return ctx.lookup(this); } }
表明邏輯「與」操做的And類,表示由兩個布爾表達式經過邏輯「與」操做給出一個新的布爾表達式的操做
package expression; public class And extends Expression { private Expression left, right; public And(Expression left, Expression right) { this.left = left; this.right = right; } @Override public boolean equals(Object obj) { if (obj != null && obj instanceof And) { return left.equals(((And) obj).left) && right.equals(((And) obj).right); } return false; } @Override public int hashCode() { return this.toString().hashCode(); } @Override public boolean interpret(Context ctx) { return left.interpret(ctx) && right.interpret(ctx); } @Override public String toString() { return "(" + left.toString() + " AND " + right.toString() + ")"; } }
表明邏輯「或」操做的Or類,表明由兩個布爾表達式經過邏輯「或」操做給出一個新的布爾表達式的操做
package expression; public class Or extends Expression { private Expression left, right; public Or(Expression left, Expression right) { this.left = left; this.right = right; } @Override public boolean equals(Object obj) { if (obj != null && obj instanceof Or) { return this.left.equals(((Or) obj).left) && this.right.equals(((Or) obj).right); } return false; } @Override public int hashCode() { return this.toString().hashCode(); } @Override public boolean interpret(Context ctx) { return left.interpret(ctx) || right.interpret(ctx); } @Override public String toString() { return "(" + left.toString() + " OR " + right.toString() + ")"; } }
表明邏輯「非」操做的Not類,表明由一個布爾表達式經過邏輯「非」操做給出一個新的布爾表達式的操做
package expression; public class Not extends Expression { private Expression exp; public Not(Expression exp) { this.exp = exp; } @Override public boolean equals(Object obj) { if (obj != null && obj instanceof Not) { return exp.equals(((Not) obj).exp); } return false; } @Override public int hashCode() { return this.toString().hashCode(); } @Override public boolean interpret(Context ctx) { return !exp.interpret(ctx); } @Override public String toString() { return "(Not " + exp.toString() + ")"; } }
環境(Context)類定義出從變量到布爾值的一個映射
package expression; import java.util.HashMap; import java.util.Map; public class Context { private Map<Variable, Boolean> map = new HashMap<Variable, Boolean>(); public void assign(Variable var, boolean value) { map.put(var, new Boolean(value)); } public boolean lookup(Variable var) throws IllegalArgumentException { Boolean value = map.get(var); if (value == null) { throw new IllegalArgumentException(); } return value.booleanValue(); } }
客戶端:
package expression; public class Client { public static void main(String[] args) { Variable x = new Variable("x"); Variable y = new Variable("y"); Context ctx = new Context(); ctx.assign(x, false); ctx.assign(y, true); System.out.println("x=" + x.interpret(ctx)); System.out.println("y=" + y.interpret(ctx)); Constant c = new Constant(true); Expression exp = new Or(new And(c, x), new And(y, new Not(x))); System.out.println(exp.toString() + "=" + exp.interpret(ctx)); } }
結果:
x=false
y=true
((true AND x) OR (y AND (Not x)))=true
意圖:給定一個語言,定義它的文法表示,並定義一個解釋器,這個解釋器使用該標識來解釋語言中的句子。
主要解決:對於一些固定文法構建一個解釋句子的解釋器。
什麼時候使用:若是一種特定類型的問題發生的頻率足夠高,那麼可能就值得將該問題的各個實例表述爲一個簡單語言中的句子。這樣就能夠構建一個解釋器,該解釋器經過解釋這些句子來解決該問題。
如何解決:構建語法樹,定義終結符與非終結符。
關鍵代碼:構建環境類,包含解釋器以外的一些全局信息,通常是 HashMap。
應用實例:編譯器、運算表達式計算。
優勢: 一、可擴展性比較好,靈活。 二、增長了新的解釋表達式的方式。 三、易於實現簡單文法。
缺點: 一、可利用場景比較少。 二、對於複雜的文法比較難維護。 三、解釋器模式會引發類膨脹。 四、解釋器模式採用遞歸調用方法。
使用場景: 一、能夠將一個須要解釋執行的語言中的句子表示爲一個抽象語法樹。 二、一些重複出現的問題能夠用一種簡單的語言來進行表達。 三、一個簡單語法須要解釋的場景。
注意事項:可利用場景比較少,JAVA 中若是碰到能夠用 expression4J、FEL表達式代替。