1. 程式人生 > >Android設計模式(九)-直譯器模式

Android設計模式(九)-直譯器模式

直譯器模式是一種行為模式,實際開發中用的很少,提供了一種解釋語言的語法或表示式的方式。

定義了一個表示式介面,通過介面解釋一個特定的上下文。類似於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

上面只是一個簡單的算術運算的解析。根據這個模式,可以增加很多運演算法則,只要建立響應的直譯器就可以。

由於直譯器和文法是一一對應的,所以一個直譯器只負責解析一種文法。我們可以為一種文法建立多個直譯器,但不能用一個直譯器解析多重文法。

直譯器值負責解析單個的文法,所以解析樹的建立就有距離的使用者去根據實際情況構建了。

總結

優點

  • 拓展性很靈活,需要增加新的直譯器的時候,直接實現一個新的就行了,然後在實際運用中靈活構建語法樹來運用。

缺點

  • 每一個文法都至少對應一個直譯器,會產生大量的類,維護困難。
  • 對於複雜的文法,要構建語法書會非常繁瑣,甚至需要構建多顆語法樹。