Android設計模式(九)-直譯器模式
阿新 • • 發佈:2019-02-13
直譯器模式是一種行為模式,實際開發中用的很少,提供了一種解釋語言的語法或表示式的方式。
定義了一個表示式介面,通過介面解釋一個特定的上下文。類似於json解析器按一定的語法解析json的。
定義
給定一個語言,定義它的文法的一種表示,並定義一個直譯器,該直譯器使用該表示來解釋語言中的句子。
什麼是文法?文法就是語言的規則,語法。每一個語法都對應一個直譯器物件,直譯器根據語法來解釋相應的句子。
舉個例子,那漢語中的主謂賓結構來說,我們經常是自己是什麼是這樣說的,我是程式設計師
,`我是設計師
…。那麼,就可以把我是[職位]
定義為一條文法。直譯器一看見這樣的文法,就能解析出來你的職業是[職業]
使用場景
- 當一個語言需要解釋執行,並且該語言中的句子可以表示為一個抽象的語法樹時。
- 某些重複出現的問題,可以用一種簡單的語言來表示的時候,可以將這個問題轉化為一種語法規則下的語句。
UML
- AbstractExpression:抽象的解析方法。宣告一個解析方法,具體實現在具體的子類中完成
- TernimalExprission:實現對文法中與終結符有關的解釋操作。
- NonterminalExpression:實現對文法中的非終結符有關的解釋操作。
- Context:包含直譯器之外的全部資訊。
- Client: 解析表示式,構建抽象語法書,執行具體的解釋操作等。
簡單實現
以數字計算為例,`a+b+c
,如果使用直譯器模式對錶達式進行解釋,那麼a,b,c可以看做是終結負號,+
看做是非終結負號。
抽象的直譯器,定義每個直譯器都有個方法提取結果值
public abstract class ArithmeticExpression {
public abstract int interpret();
}
數字直譯器,僅僅解釋數字
public class NumExpression extends ArithmeticExpression {
private int num;
public NumExpression(int num) {
this.num = num;
}
@Override
public int interpret() {
return num;
}
}
抽象的運算子直譯器,要求傳入兩個引數進行計算
public abstract class OperatorExpression extends ArithmeticExpression {
protected ArithmeticExpression exp1,exp2;
public OperatorExpression(ArithmeticExpression exp1, ArithmeticExpression exp2) {
this.exp1 = exp1;
this.exp2 = exp2;
}
}
一個具體的加法運算子,解析出兩個引數的和
public class AddExpression extends OperatorExpression {
public AddExpression(ArithmeticExpression exp1, ArithmeticExpression exp2) {
super(exp1, exp2);
}
@Override
public int interpret() {
return exp1.interpret()+exp2.interpret();
}
}
一個計算類,處理與解析相關的業務
public class Calculator {
private Stack<ArithmeticExpression> mExp = new Stack<>();
//使用的時候在構造方法裡傳入要計算的字串。
public Calculator(String expression) {
//準備兩個直譯器
ArithmeticExpression exp1, exp2;
//將字串按空格分割
String[] elements = expression.split(" ");
for (int i = 0; i < elements.length; i++) {
switch (elements[i].charAt(0)) {
case '+':
//如果是’+‘,說明是個運算子,將上一個數和下一個數相加,也就是當前下標i的i-1和i+1相加。
//去除棧中前一次壓如的數。也就是這個加號之前的計算結果。
exp1 = mExp.pop();
//取出這個加號後面的數,解析成數字,
exp2 = new NumExpression(Integer.valueOf(elements[++i]));
//將這兩個數進行計算,並將結果壓入棧中。
mExp.push(new AddExpression(exp1,exp2));
break;
default:
//如果是數字,就解析為數字,加入棧中。
mExp.push(new NumExpression(Integer.valueOf(elements[i])));
break;
}
}
}
public int calculate(){
return mExp.pop().interpret();
}
}
客戶端呼叫
public class Client {
public static void main(String[] args) {
Calculator calculator = new Calculator("1 + 2 + 3");
System.out.println(calculator.calculate());
}
}
可以看到輸出結果是
6
如果需要加上減號運算子呢?就子啊實現一個減號的解析器:
public class Subtraction extends OperatorExpression {
public Subtraction(ArithmeticExpression exp1, ArithmeticExpression exp2) {
super(exp1, exp2);
}
@Override
public int interpret() {
return exp1.interpret()-exp2.interpret();
}
}
然後在計算的類里加一種判斷:
case '-':
exp1 = mExp.pop();
exp2 = new NumExpression(Integer.valueOf(elements[++i]));
mExp.push(new SubtractionExpression(exp1,exp2));
break;
客戶端呼叫
public class Client {
public static void main(String[] args) {
Calculator calculator = new Calculator("1 + 2 + 3 - 1");
System.out.println(calculator.calculate());
}
}
可以看到輸出結果變成了
5
上面只是一個簡單的算術運算的解析。根據這個模式,可以增加很多運演算法則,只要建立響應的直譯器就可以。
由於直譯器和文法是一一對應的,所以一個直譯器只負責解析一種文法。我們可以為一種文法建立多個直譯器,但不能用一個直譯器解析多重文法。
直譯器值負責解析單個的文法,所以解析樹的建立就有距離的使用者去根據實際情況構建了。
總結
優點
- 拓展性很靈活,需要增加新的直譯器的時候,直接實現一個新的就行了,然後在實際運用中靈活構建語法樹來運用。
缺點
- 每一個文法都至少對應一個直譯器,會產生大量的類,維護困難。
- 對於複雜的文法,要構建語法書會非常繁瑣,甚至需要構建多顆語法樹。