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

設計模式(九) — 直譯器模式

化繁為簡的翻譯機——直譯器模式

直譯器模式的定義

給定一個語言,定義它的文法的一種表示,並定義一個直譯器,該直譯器使用該表示來解釋語言中的句子。其實就是自己定義一個規則,將一個語言解釋清楚。

直譯器模式的使用場景

  1. 如果某個簡單的語言需要解釋執行而且可以將該語言中的語句表示為一個抽象語法樹時,可以考慮使用直譯器模式。
  2. 在某些特定的領域出現不斷重複的問題時,可以將該領域的問題轉化為一種語法規則下的語句,然後構建直譯器來解釋該語句。

直譯器模式的例項

直譯器模式在實際運用上相對來說要少很多,因為我們很少自己器構造一個語言,但是它的應用範圍相當廣泛。我們來對一個算術表示式運用直譯器模式,如“m + n + p”,m、n、p代表數字,‘+’代表運算子。

抽象算術運算直譯器 ArithmeticExpression.class

public abstract class ArithmeticExpression {
    /**
     * description: 抽象的解析方法
     * 具體的解析邏輯由具體的子類實現
     */
    public abstract int interpret();
}

數字直譯器 NumExpression.class

public class NumExpression extends ArithmeticExpression {
    private int num;

    public
NumExpression(int num) { this.num = num; } @Override public int interpret() { return 0; } }

運算子號抽象直譯器 OperatorExpression.class

public abstract class OperatorExpression extends ArithmeticExpression{
    //宣告兩個成員變數儲存運算子號兩邊的數字直譯器
    protected ArithmeticExpression expression1,
expression2; public OperatorExpression(ArithmeticExpression expression1, ArithmeticExpression expression2) { this.expression1 = expression1; this.expression2 = expression2; } }

加法運算直譯器 AdditionExpression.class

public class AdditionExpression extends OperatorExpression {
    public AdditionExpression(ArithmeticExpression expression1, ArithmeticExpression expression2) {
        super(expression1, expression2);
    }

    @Override
    public int interpret() {
        return expression1.interpret() + expression2.interpret();
    }
}

減法運算直譯器 SubtractionExpression.class

public class SubtractionExpression extends OperatorExpression {
    public SubtractionExpression(ArithmeticExpression expression1, ArithmeticExpression expression2) {
        super(expression1, expression2);
    }

    @Override
    public int interpret() {
        return expression1.interpret() - expression2.interpret();
    }
}

處理與解釋 Calculator.class

public class Calculator {
    //宣告一個Stack棧儲存並操作所有相關的直譯器
    private Stack<ArithmeticExpression> expressionStack = new Stack<>();

    public Calculator(String expression) {
        //宣告兩個ArithmeticExpression型別的臨時變數,儲存運算子左右兩邊的數字直譯器
        ArithmeticExpression expression1, expression2;

        //根據空格分隔表示式字串
        String[] elements = expression.split(" ");

        //迴圈遍歷表示式元素陣列
        for (int i = 0; i < elements.length; i++) {
            //判斷運算子
            switch (elements[i].charAt(0)) {
                case '+'://如果是加號
                    //則將佔中的直譯器彈出作為運算子號左邊的直譯器
                    expression1 = expressionStack.pop();
                    //同時將運算子號陣列下標下一個元素構造為一個數字直譯器
                    expression2 = new NumExpression(Integer.valueOf(elements[++i]));
                    //通過上面兩個數字直譯器構造加法運算直譯器
                    expressionStack.push(new AdditionExpression(expression1, expression2));
                    break;
                case '-'://如果是減號
                    expression1 = expressionStack.pop();
                    expression2 = new NumExpression(Integer.valueOf(elements[++i]));
                    expressionStack.push(new SubtractionExpression(expression1, expression2));
                    break;
                default:
                    //如果不是運算子則為數字,若是數字,直接構造數字直譯器並壓入棧
                    expressionStack.push(new NumExpression(Integer.valueOf(elements[i])));
                    break;
            }
        }
    }

    /**
     * description: 計算結果
     */
    public int calculate() {
        return expressionStack.pop().interpret();
    }
}

呼叫:

        Calculator calculator = new Calculator("153 + 118 - 555 + 3598 - 597 - 66");
        Log.i(TAG, "result = " + calculator.calculate());

將一條具體的文法通過一個直譯器解釋,把複雜的文法規則分離為簡單的功能進行解釋,最後將其組合一顆抽象語法樹解釋執行,至此,我們可以看到直譯器模式的原理與本質:將複雜的問題簡單化、模組化、分離實現、解釋執行。

小結

優點

最大的有點就是其靈活的擴充套件性,當我們想對文法規則進行擴充套件延伸時,只需要增加直譯器。

缺點

每一條文法都有可以對應至少一個直譯器,其會生成大量的類,導致後期維護困難。同時對於過於複雜的文法,構建其抽象語法樹會顯得異常煩瑣。