天天看點

設計模式-解釋器模式(Interpreter Pattern)

推薦:​​Java設計模式彙總​​

解釋器模式

定義

Given a language,define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.

給定一個語言,定義它的文法的一種表示,并定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。

類型

行為型。

角色

  • AbstractExpression(抽象表達式):在抽象表達式中聲明抽象的解釋操作,它是所有終結符表達式和非終結符表達式的公共父類。
  • TerminalExpression(終結符表達式):終結符表達式是抽象表達式的子類,它實作了與文法中的終結符相關聯的解釋操作,在句子中的每一個終結符都是該類的一個執行個體。通常在一個解釋器模式中隻有少數幾個終結符表達式類,它們的執行個體可以通過非終結符表達式組成較為複雜的句子。
  • NonterminalExpression(非終結符表達式):非終結符表達式也是抽象表達式的子類,它實作了文法中非終結符的解釋操作,由于在非終結符表達式中可以包含終結符表達式,也可以繼續包含非終結符表達式,是以其解釋操作一般通過遞歸的方式來完成。
  • Context(環境類):環境類又稱為上下文類,它用于存儲解釋器之外的一些全局資訊,通常它臨時存儲了需要解釋的語句。

例子

這裡舉計算字尾表達式的例子,為了更加簡化,這裡隻處理兩種運算符​​

​+​

​​、​

​*​

​,沒有括号,并且預設字尾表達式是正确的。

Interpreter接口(抽象表達式)。

package com.kaven.design.pattern.behavioral.interpreter;

public interface Interpreter {
    int interpret();
}      

NumberInterpreter類(非終結符表達式),實作了Interpreter接口。

package com.kaven.design.pattern.behavioral.interpreter;

public class NumberInterpreter implements Interpreter {
    private int number;
    public NumberInterpreter(int number){
        this.number = number;
    }
    public NumberInterpreter(String number){
        this.number = Integer.parseInt(number);
    }
    public int interpret() {
        return this.number;
    }
}      

AddInterpreter類(終結符表達式),實作了Interpreter接口。

package com.kaven.design.pattern.behavioral.interpreter;

public class AddInterpreter implements Interpreter {

    private Interpreter firstExpression,secondExpression;

    public AddInterpreter(Interpreter firstExpression, Interpreter secondExpression) {
        this.firstExpression = firstExpression;
        this.secondExpression = secondExpression;
    }

    public int interpret() {
        return this.firstExpression.interpret()+this.secondExpression.interpret();
    }

    public String toString(){
        return "+";
    }
}      

MultiInterpreter類(終結符表達式),實作了Interpreter接口。

package com.kaven.design.pattern.behavioral.interpreter;

public class MultiInterpreter implements Interpreter {
    private Interpreter firstExpression,secondExpression;

    public MultiInterpreter(Interpreter firstExpression, Interpreter secondExpression) {
        this.firstExpression = firstExpression;
        this.secondExpression = secondExpression;
    }

    public int interpret() {
        return this.firstExpression.interpret()*this.secondExpression.interpret();
    }

    public String toString(){
        return "*";
    }
}      

OperatorUtil類(操作工具類)。

package com.kaven.design.pattern.behavioral.interpreter;

public class OperatorUtil {
    public static boolean isOperator(String symbol){
        return (symbol.equals("+") || symbol.equals("*"));
    }

    public static Interpreter getExpressionObject(Interpreter firstExpression ,
                                                  Interpreter secondExpression ,
                                                  String symbol){
        if(symbol.equals("+")){
            return new AddInterpreter(firstExpression , secondExpression);
        }
        else if(symbol.equals("*")){
            return new MultiInterpreter(firstExpression , secondExpression);
        }
        return null;
    }
}      

KavenExpressionParser類,這裡使用了上面實作的解釋器模式相關類和​

​Stack​

​實作了計算字尾表達式的邏輯,計算字尾表達式的算法應該是資料結構的基礎吧,這裡就不講了。

package com.kaven.design.pattern.behavioral.interpreter;

import java.util.Stack;

public class KavenExpressionParser {
    private Stack<Interpreter> stack = new Stack<Interpreter>();

    public int parse(String str){
        String[] strItemArray = str.split(" ");
        for(String symbol : strItemArray){
            if(!OperatorUtil.isOperator(symbol)){
                Interpreter numberExpression = new NumberInterpreter(symbol);
                stack.push(numberExpression);
                System.out.println(String.format("入棧:%d",numberExpression.interpret()));
            }
            else{
                // 是運算符号可以計算
                Interpreter firstExpression = stack.pop();
                Interpreter secondExpression = stack.pop();
                System.out.println(String.format("出棧:%d 和 %d",
                        firstExpression.interpret(),secondExpression.interpret()));
                Interpreter operator = OperatorUtil.getExpressionObject(firstExpression ,
                        secondExpression , symbol);
                System.out.println(String.format("應用運算符: %s",operator));
                int result = operator.interpret();
                NumberInterpreter resultExpression = new NumberInterpreter(result);
                stack.push(resultExpression);
                System.out.println(String.format("階段結果入棧:%d",resultExpression.interpret()));
            }
        }
        int result  = stack.pop().interpret();
        return result;
    }
}      

Expression類(環境類),存儲字尾表達式。

package com.kaven.design.pattern.behavioral.interpreter;

public class Expression {
    private  String expression;

    public Expression(String expression) {
        this.expression = expression;
    }

    public  String getExpression() {
        return this.expression;
    }
}      

應用層代碼:

package com.kaven.design.pattern.behavioral.interpreter;

public class Test {
    public static void main(String[] args) {
        Expression expression = new Expression("6 100 11 + *");
        KavenExpressionParser expressionParser = new KavenExpressionParser();
        int result = expressionParser.parse(expression.getExpression());
        System.out.println("解釋器計算結果"+result);
    }
}      

輸出:

入棧:6
入棧:100
入棧:11
出棧:11 和 100
應用運算符: +
階段結果入棧:111
出棧:111 和 6
應用運算符: *
階段結果入棧:666
解釋器計算結果666      

這裡便完成了一個簡單的解釋器模式例子。

适用場景

  • 可以将一個需要解釋執行的語言中的句子表示為一個抽象文法樹。
  • 一些重複出現的問題可以用一種簡單的語言進行表達。
  • 執行效率不是關鍵問題。高效的解釋器通常不是通過直接解釋抽象文法樹來實作的,而是需要将它們轉換成其他形式,使用解釋器模式的執行效率并不高。

優點

  • 易于改變和擴充文法。由于在解釋器模式中使用類表示語言的文法規則,是以可以通過繼承等機制來改變或擴充文法。
  • 實作文法較為容易。在抽象文法樹中每一個表達式節點類的實作方式都是相似的,這些類的代碼編寫都不會特别複雜,還可以通過一些工具自動生成節點類代碼。
  • 解釋器模式會引起類膨脹。
  • 解釋器模式将會導緻系統比較複雜, 為維護帶來了非常多的麻煩。
  • 執行效率低。由于在解釋器模式中一般采用了大量的循環和遞歸調用(我們的例子是使用棧來代替遞歸),是以在解釋較為複雜的句子時其速度很慢,而且代碼的調試過程也比較麻煩。

繼續閱讀