17、解釋器模式(Interpreter Pattern)

1. 解釋器模式

1.1 簡介

??Interpreter(解釋器)模式是對特定的計算機程序設計語言,用來解釋預先定義的文法。Interpreter模式是一種簡單的語法解釋器,屬于行為模式。給定一個語言,定義它的文法的一種表示,并定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。

??編譯原理上的編譯器分為詞法分析器、語法分析器、語義分析器、中間代碼優(yōu)化器以及最終的最終代碼生成器幾個部分。而這個解釋器其實就是完成了對語法的解析,將一個個的詞組解釋成了一個個語法范疇,之后拿來使用。Interpreter模式的目的就是使用一個解釋器為用戶提供一個一門定義語言的語法表示的解釋器,然后通過這個解釋器來解釋語言中的句子。提供了一個實現(xiàn)語法解釋器的框架。

??Interpreter(解釋器)模式大多用來解釋一些(自定義的)獨特語法,例如某些游戲開發(fā)引擎中讀取XML文件,或是WindowsPhone開發(fā)中的XAML文件,都是使用此模式來進行的。與其說是一種模式,不如說是一種具有通用規(guī)范的行為更為準確。

1.2 結構

Interpreter 模式uml:

Interpreter 模式uml.png

Interpreter 模式角色:

  • AbstractInterpreter 定義interpret操作,聲明一個所有的具體表達式都需要實現(xiàn)的抽象接口;這個接口主要是一個interpret()方法,稱做解釋操作。

  • TerminalInterpreter 終態(tài)節(jié)點,實現(xiàn)了抽象表達式所要求的接口;文法中的每一個終結符都有一個具體終結表達式與之相對應。比如公式R=R1+R2,R1和R2就是終結符,對應的解析R1和R2的解釋器就是終結符表達式。

  • NonTerminalInterpreter 非終態(tài)節(jié)點,內(nèi)部調(diào)用其他的Interpreter。文法中的每一條規(guī)則都需要一個具體的非終結符表達式,非終結符表達式一般是文法中的運算符或者其他關鍵字,比如公式R=R1+R2中,“+"就是非終結符,解析“+”的解釋器就是一個非終結符表達式。

  • Context 上下文對象,存放所有Interpreter共享的信息。一般是用來存放文法中各個終結符所對應的具體值,比如R=R1+R2,給R1賦值100,給R2賦值200,這些信息需要存放到環(huán)境中。

2. Interpreter 模式示例

??Interpreter 模式的示例代碼很簡單, 只是為了說明模式的組織和使用, 實際的解釋Interpret 邏輯沒有實際提供。

??Interpreter模式和Composite模式相似,最終將構造為一顆語法樹。以算術表達式"20(3+1)-45+3"為例,用Interpreter 模式寫一個計算表達式的示例。

Interpreter接口:

public interface Interpreter {
    double calculate(String expression);
}

終態(tài)Interpreter:

public class Number implements Interpreter {

    private double number;

    public Number(double number) {
        super();
        this.number = number;
    }

    @Override
    public double calculate(String expression) {
        return number;
    }

}

非終態(tài)加法Interpreter:

public class Add implements Interpreter {

    private Interpreter left;

    private Interpreter right;

    public Add(Interpreter left, Interpreter right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public double calculate(String expression) {
        return left.calculate(expression) + right.calculate(expression);
    }

}

解釋器ParserInterpreter(解析算術表達式,構造語法樹):

public class ParserInterpreter implements Interpreter {

    @Override
    public double calculate(String expression) { //1 * (2 + 3)
        StringBuilder number = new StringBuilder();
        LinkedList<Interpreter> interpreters = new LinkedList<>();
        LinkedList<Character> operators = new LinkedList<>();
        for (char ch : expression.toCharArray()) {
            if (isOperator(ch)) {
                //將之前的數(shù)字入棧
                if (number.length() > 0) {
                    interpreters.add(new Number(Double.parseDouble(number.toString())));
                    number.setLength(0);
                }
                //組裝表達式
                while (interpreters.size() >= 2) {
                    Character lastOp = operators.getLast();
                    //碰到左括號
                    if (isOpenParen(lastOp)) {
                        break;
                    }
                    //碰到了運算符,但下一個運算符優(yōu)先級是否更高?
                    if (rightOperatorGreater(lastOp, ch)) {
                        break;
                    }
                    Interpreter right = interpreters.removeLast();
                    Interpreter left = interpreters.removeLast();
                    Interpreter interpreter = constructExpression(left, 
                            operators.removeLast(), right);
                    interpreters.addLast(interpreter);
                }
                if (isCloseParen(ch)) {
                    //碰到右括號,直接去掉左括號
                    operators.removeLast();
                } else {
                    //非右括號,直接進棧
                    operators.addLast(ch);
                }
            } else {
                number.append(ch);
            }
        }
        //最后是數(shù)字,如1*2+3
        if (number.length() > 0) {
            interpreters.add(new Number(Double.parseDouble(number.toString())));
            number.setLength(0);
        }
        //最后一次運算
        if (operators.size() > 0) {
            Interpreter right = interpreters.removeLast();
            Interpreter left = interpreters.removeLast();
            Interpreter interpreter = constructExpression(left, 
                    operators.removeLast(), right);
            interpreters.addLast(interpreter);
        }
        //調(diào)用組裝好的樹
        return interpreters.pop().calculate(expression);
    }
    
    /**
     * 右邊運算符是否優(yōu)先級更高
     */
    private boolean rightOperatorGreater(char leftOp, char rightOp) {
        if (rightOp == '*' || rightOp == '/') {
            return leftOp == '+' || leftOp == '-';
        }
        return false;
    }
    
    private boolean isOperator(char ch) {
        return ch == '-' || ch == '+' || ch == '/' || ch == '*' || ch == '(' || ch ==')';
    }
    
    private boolean isOpenParen(char ch) {
        return ch == '(';
    }
    
    private boolean isCloseParen(char ch) {
        return ch == ')';
    }
    
    private Interpreter constructExpression(Interpreter left, char op, Interpreter right) {
        switch (op) {
        case '+' :
            return new Add(left, right);
        case '-' :
            return new Sub(left, right);
        case '*' :
            return new Plus(left, right);
        case '/' :
            return new Divide(left, right);
        default:
            break;
        }
        return null;
    }

}

調(diào)用示例:

    public static void main(String[] args) {
        ParserInterpreter interpreter = new ParserInterpreter();
        double result = interpreter.calculate("20*(3+1)-4*5+3");
        System.out.println("計算結果為: " + result);
    }

3. 總結

Interpreter 模式優(yōu)點:

  • 易于改變和擴展文法。由于在解釋器模式中使用類來表示語言的文法規(guī)則,因此可以通過繼承等機制來改變或擴展文法。

  • 每一條文法規(guī)則都可以表示為一個類,因此可以方便地實現(xiàn)一個簡單的語言。

  • 實現(xiàn)文法較為容易。在抽象語法樹中每一個表達式節(jié)點類的實現(xiàn)方式都是相似的,這些類的代碼編寫都不會特別復雜,還可以通過一些工具自動生成節(jié)點類代碼。

  • 增加新的解釋表達式較為方便。如果用戶需要增加新的解釋表達式只需要對應增加一個新的終結符表達式或非終結符表達式類,原有表達式類代碼無須修改,符合“開閉原則”。

Interpreter 模式缺點:

  • 對于復雜文法難以維護。在解釋器模式中,每一條規(guī)則至少需要定義一個類,因此如果一個語言包含太多文法規(guī)則,類的個數(shù)將會急劇增加,導致系統(tǒng)難以管理和維護,此時可以考慮使用語法分析程序等方式來取代解釋器模式。

  • 執(zhí)行效率較低。由于在解釋器模式中使用了大量的循環(huán)和遞歸調(diào)用,因此在解釋較為復雜的句子時其速度很慢,而且代碼的調(diào)試過程也比較麻煩。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容