介紹
解釋器模式是一種使用的比較少的一種模式纷纫,其提供了一種解釋語(yǔ)言的語(yǔ)法或表達(dá)式的方式涕蚤,該模式定義了一個(gè)表達(dá)式接口卵酪,通過(guò)該接口解釋一個(gè)特定的上下文。(比較抽象熙掺,想了解建議查看原版書(shū)籍)
使用場(chǎng)景
- 如果某個(gè)簡(jiǎn)單的語(yǔ)言需要解釋執(zhí)行未斑,而且可以將該語(yǔ)言中的語(yǔ)句表示為一個(gè)抽象語(yǔ)法樹(shù)時(shí),可以考慮使用該模式币绩。比如有一個(gè)簡(jiǎn)單的數(shù)學(xué)表達(dá)式:p+q+m-n颂碧。
- 在某些特定領(lǐng)域出現(xiàn)不斷重復(fù)的問(wèn)題時(shí),可以將該領(lǐng)域的問(wèn)題轉(zhuǎn)換為一種語(yǔ)法規(guī)則下的語(yǔ)句类浪,然后構(gòu)建解釋器來(lái)解釋該語(yǔ)句载城。比如需要將一段阿拉伯?dāng)?shù)字轉(zhuǎn)換為中文的數(shù)字,又或者將某個(gè)小寫(xiě)英文短語(yǔ)轉(zhuǎn)換為大寫(xiě)费就。
UML 類(lèi)圖
角色介紹诉瓦。
AbstractExpression: 抽象表達(dá)式。
聲明一個(gè)抽象的解釋操作父類(lèi)力细,并定義一個(gè)抽象的解釋方法睬澡,其具體的實(shí)現(xiàn)在各個(gè)具體的子類(lèi)解釋器中完成。
TerminalExpression: 終結(jié)符表達(dá)式眠蚂。
實(shí)現(xiàn)文法與終結(jié)符有關(guān)的解釋操作煞聪。文法中每一個(gè)終結(jié)符都有一個(gè)具體的終結(jié)表達(dá)式與之對(duì)應(yīng)。
NonternimalExpression: 非終結(jié)符表達(dá)式逝慧。
實(shí)現(xiàn)文法中與非終結(jié)符有關(guān)的解釋操作昔脯。
Context: 上下文環(huán)境類(lèi)。
包含解釋器之外的全局信息笛臣。
Client: 客戶(hù)類(lèi)云稚。
解析表達(dá)式,構(gòu)建抽象語(yǔ)法樹(shù)沈堡,執(zhí)行具體的解釋操作等静陈。
對(duì)應(yīng)通用模式代碼
// 抽象表達(dá)式
public abstract class AbstractExpression {
/**
* 抽象的解析方法
*/
public abstract void interpret(Context ctx);
}
// 終結(jié)符表達(dá)式
public class TerminalExpression extends AbstractExpression {
@Override
public void interpret(Context ctx) {
//實(shí)現(xiàn)文法中與終結(jié)符有關(guān)的解釋操作
}
}
// 非終結(jié)符表達(dá)式
public class NonterminalExpression extends AbstractExpression {
@Override
public void interpret(Context ctx) {
//實(shí)現(xiàn)文法中與非終結(jié)符有關(guān)的解釋操作
}
}
// 上下文環(huán)境類(lèi),包含解釋器之外的全局信息
public class Context {
}
// 客戶(hù)類(lèi)
public class Client {
public static void main(String[] args) {
//根據(jù)文法對(duì)特定句子構(gòu)建抽象語(yǔ)法樹(shù)后解釋
}
}
簡(jiǎn)單實(shí)現(xiàn)
需求:比如算數(shù)表達(dá)式 m + n + p。代表數(shù)字的m鲸拥、n拐格、p三個(gè)字符看成終結(jié)符號(hào),+ 看做非終結(jié)符號(hào)刑赶。
-
抽象的算術(shù)運(yùn)算解釋器禁荒,為所有解釋器共性的提取
public abstract class ArithmeticExpression { /** * 抽象的解析方法 * 具體的解析邏輯由具體的子類(lèi)實(shí)現(xiàn) * @return 解析得到具體的值 */ public abstract int interpret(); }
-
數(shù)字解釋器,僅僅為了解釋數(shù)字
public class NumExpression extends ArithmeticExpression{ private int num; public NumExpression(int num) { this.num = num; } @Override public int interpret() { return num; } }
-
運(yùn)算符號(hào)抽象解釋器角撞,為所有運(yùn)算符號(hào)解釋器共性的提取
public abstract class OperatorExpression extends ArithmeticExpression { //聲明兩個(gè)成員變量存儲(chǔ)運(yùn)算符號(hào)兩邊的數(shù)字解釋器 protected ArithmeticExpression exp1, exp2; public OperatorExpression(ArithmeticExpression exp1, ArithmeticExpression exp2) { this.exp1 = exp1; this.exp2 = exp2; } }
-
加法運(yùn)算抽象解釋器
public class AdditionExpression extends OperatorExpression { public AdditionExpression(ArithmeticExpression exp1, ArithmeticExpression exp2) { super(exp1, exp2); } @Override public int interpret() { return exp1.interpret() + exp2.interpret(); } }
-
處理與解釋相關(guān)的一些業(yè)務(wù)
public class Calculator { //聲明一個(gè)Stack棧存儲(chǔ)并操作所有相關(guān)的解釋器 private Stack<ArithmeticExpression> mExpStack = new Stack<>(); public Calculator(String expression) { //聲明兩個(gè)TerminalExpression類(lèi)型的臨時(shí)變量呛伴,存儲(chǔ)運(yùn)算符左右兩邊的數(shù)字解釋器 ArithmeticExpression exp1, exp2; String[] elements = expression.split(" "); //循環(huán)遍歷表達(dá)式元素?cái)?shù)組 for (int i = 0; i < elements.length; i++) { //判斷運(yùn)算符號(hào) switch (elements[i].charAt(0)) { case '+': //如果是加號(hào) //將棧中的解釋器彈出作為運(yùn)算符號(hào)右邊的解釋器 exp1 = mExpStack.pop(); //同時(shí)將運(yùn)算符號(hào)數(shù)組下標(biāo)下一個(gè)元素構(gòu)造為一個(gè)數(shù)字解釋器 exp2 = new NumExpression(Integer.valueOf(elements[++i])); //通過(guò)尚明兩個(gè)數(shù)字解釋器構(gòu)造加法運(yùn)算解釋器 mExpStack.push(new AdditionExpression(exp1, exp2)); break; default: //如果是數(shù)字 //直接構(gòu)造數(shù)字解釋器并壓入棧 mExpStack.push(new NumExpression(Integer.valueOf(elements[i]))); break; } } } public int calculate() { return mExpStack.pop().interpret(); } }
-
客戶(hù)類(lèi)
public class Client { public static void main(String[] args) { Calculator calculator = new Calculator("1 + 2 + 3 + 10"); System.out.println(calculator.calculate()); } }
此時(shí)只是定義了加法運(yùn)算,如果需要增加減法運(yùn)算谒所,則可以在 Calculator 中增加以下分支
case '-': //如果是減號(hào)
exp1 = mExpStack.pop();
exp2 = new NumExpression(Integer.valueOf(elements[++i]));
mExpStack.push(new SubtractionExpression(exp1, exp2));
break;
此時(shí)热康,在 Client 中就可以開(kāi)始使用了
public class Client {
public static void main(String[] args) {
Calculator calculator = new Calculator("1 - 2 - 3 + 10");
System.out.println(calculator.calculate());
}
}
Android 源碼中的實(shí)現(xiàn)
AndroidManifest.xml 配置文件的讀取。
總結(jié)
-
優(yōu)點(diǎn)
靈活的擴(kuò)展性劣领,當(dāng)我們相對(duì)文法規(guī)則進(jìn)行擴(kuò)展延伸時(shí)姐军,只需要增加相應(yīng)的非終結(jié)符解釋器,并在構(gòu)建抽象語(yǔ)法樹(shù)時(shí)尖淘,使用到新增的解釋器對(duì)象進(jìn)行具體的解釋即可奕锌。
-
缺點(diǎn)
每一條文法都可以對(duì)應(yīng)至少一個(gè)解釋器,其會(huì)生成大量的類(lèi)村生,導(dǎo)致后期維護(hù)困難惊暴;同時(shí),對(duì)于復(fù)雜的文法趁桃,構(gòu)建其抽象語(yǔ)法樹(shù)會(huì)顯得異常繁瑣辽话,甚至有可能會(huì)出現(xiàn)需要構(gòu)建多棵抽象語(yǔ)法樹(shù)的情況,因此卫病,對(duì)于復(fù)雜的文法并不推薦使用解釋器模式油啤。