一、介紹
解釋器模式(Interpreter Pattern)是一種用的比較少的行為型模式,其提供了一種解釋語言的語法或表達式的方式,該模式定義了一個表達式接口,通過該接口解釋一個特定的上下文。在這么多的設計模式中,解釋器模式在實際運用上相對來說要少很多,因為我們很少會自己去構造一個語言的文法。雖然如此,既然它能夠在設計模式中有一席之位,那么必定有它的可用之處。
二、定義
給定一個語言,定義它的文法的一種表示,并定義一個解釋器,該解釋器使用該表示來解釋語言中的句子。(其中語言就是我們需要解釋的對象,文法就是這個語言的規律,解釋器就是翻譯機,通過文法來翻譯語言。)
三、使用場景
如果某個簡單的語言需要解釋執行而且可以將該語言中的語句表示為一個抽象的語法樹時可以考慮使用解釋器模式。
在某些特定的領域出現不斷重復的問題時,可以將該領域的問題轉化為一種語法規則下的語句,然后構建解釋器來解釋該語句。
四、解釋器模式的UML類圖
UML類圖:
角色介紹:
AbstractExpression:抽象表達式,聲明一個抽象的解釋操作父類,并定義一個抽象的 interpret() 解釋方法,其具體的實現在各個具體的子類解釋器中完成。
TerminalExpression:終結符表達式,實現了抽象表達式角色所要求的接口,主要是一個interpret()方法;文法中的每一個終結符都有一個具體終結表達式與之相對應。比如有一個簡單的公式R=R1+R2,在里面R1和R2就是終結符,對應的解析R1和R2的解釋器就是終結符表達式。
NonterminalExpression:非終結符表達式,文法中的每一條規則都需要一個具體的非終結符表達式,非終結符表達式一般是文法中的運算符或者其他關鍵字,比如公式R=R1+R2中,“+”就是非終結符,解析“+”的解釋器就是一個非終結符表達式。
Context:上下文環境類,這個角色的任務一般是用來存放文法中各個終結符所對應的具體值,比如R=R1+R2,我們給R1賦值100,給R2賦值200。這些信息需要存放到環境角色中,很多情況下我們使用Map來充當環境角色就足夠了。
Client:客戶類,解析表達式,構建抽象語法樹,執行具體的解釋操作等。
通用代碼如下:
/*** 抽象表達式*/
public abstract class AbstractExpression {/*** 抽象的解析方法* @param context 上下文環境對象*/public abstract void interpret(Context context);
}/*** 終結符表達式*/
public class TerminalExpression extends AbstractExpression{@Overridepublic void interpret(Context context) {//實現文法中與終結符有關的解釋操作}
}/*** 非終結符表達式*/
public class NonterminalExpression extends AbstractExpression{@Overridepublic void interpret(Context context) {//實現文法中與非終結符有關的解釋操作}
}/*** 上下文環境類*/
public class Context {
}/*** 客戶類*/
public class Client {public static void main(String[] args) {//根據文法對特定句子構建抽象語法樹后解釋}
}
五、簡單實現
我們使用解釋器模式對“m+n+p”這個表達式進行解釋,那么代表數字的m、n和p就可以看成終結符號,而“+”這個運算符號可以當做非終結符號。
抽象的算數運算解釋器:
public abstract class ArithemticExpression {/*** 抽象的解析方法 * 具體的解析邏輯由具體的子類實現* * @return 解析得到具體的值*/public abstract int interpreter();
}
數字解釋器:
public class NumExpression extends ArithemticExpression{private int num;public NumExpression(int num){this.num = num;}@Overridepublic int interpreter() {return num;}
}
運算符號解釋器:
public abstract class OperatorExpression extends ArithemticExpression{protected ArithemticExpression exp1, exp2;public OperatorExpression(ArithemticExpression exp1, ArithemticExpression exp2){this.exp1 = exp1;this.exp2 = exp2;}
}
具體的加法運算符解釋器:
public class AdditionExpression extends OperatorExpression{public AdditionExpression(ArithemticExpression exp1,ArithemticExpression exp2) {super(exp1, exp2);}@Overridepublic int interpreter() {return exp1.interpreter() + exp2.interpreter();}}
處理解釋器:
public class Calculator {//聲明一個Stack棧儲存并操作所有相關的解釋器private Stack<ArithemticExpression> mExpStack = new Stack<ArithemticExpression>();public Calculator(String expression){//聲明兩個ArithemticExpression類型的臨時變量,儲存運算符左右兩邊的數字解釋器ArithemticExpression exp1,exp2;//根據空格分割表達式字符串(比如1 + 2 + 3 + 4)String[] elements = expression.split(" ");/** 遍歷表達式元素數組*/for(int i = 0; i < elements.length; i++){/** 判斷運算符號*/switch (elements[i].charAt(0)) {case '+'://如果是加號,則將棧中的解釋器彈出作為運算符號左邊的解釋器exp1 = mExpStack.pop();//同時將運算符號數組下標的下一個元素構造為一個數字解釋器exp2 = new NumExpression(Integer.parseInt(elements[++i]));//通過上面的兩個數字解釋器構造加法運算解釋器 mExpStack.push(new AdditionExpression(exp1, exp2));break;default:/** 如果為數字,直接構造數字解釋器并壓入棧*/mExpStack.push(new NumExpression(Integer.valueOf(elements[i])));break;}}}/*** 計算結果* * @return 最終的計算結果*/public int calculate(){return mExpStack.pop().interpreter();}
}
調用:
public class Client {public static void main(String[] args) {Calculator c = new Calculator("22 + 553 + 83 + 5");System.out.println("計算結果:"+c.calculate());}
}
結果:
計算結果:663
如果相加如減法的操作,在Calculator中加入相應判斷即可:
public class SubtractionExpression extends OperatorExpression{public SubtractionExpression(ArithemticExpression exp1,ArithemticExpression exp2) {super(exp1, exp2);}@Overridepublic int interpreter() {return exp1.interpreter() - exp2.interpreter();}}
Calculator中加入:
case '-':exp1 = mExpStack.pop();exp2 = new NumExpression(Integer.parseInt(elements[++i]));mExpStack.push(new SubtractionExpression(exp1, exp2));break;
從上面可以看出解釋器模式很靈活,他將復雜問題可以簡單化、模塊化、分離實現、解釋執行。
六、Android源碼中解釋器模式
1、PackageParser
PackageParser是對AndroidManifest.xml配置文件進行讀取的,具體原理參考:解析AndroidManifest原理
七、總結
優點:
- 最大的優點使其靈活的擴展性,當我們想對文法規則進行擴展延伸時,只需要增加相應的非終結符解釋器,并在構建抽象語法樹時,使用到新增的解釋器對象進行具體的解釋即可,非常方便。
缺點:
每個語法都要產生一個非終結符表達式,語法規則比較復雜時,就可能產生大量的類文件,為維護帶來了非常多的麻煩。
解釋器模式由于使用了大量的循環和遞歸,效率是個問題,特別是用于解析復雜、冗長的語法時,效率是難以忍受的。