設計模式之解釋器模式

0x01.定義與類型

  • 定義:給定一個語言,定義它的文法的一種表示,並定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。
  • 爲了解釋一種語言,而爲語言建立的解釋器。
  • 類型:行爲型
  • UML類圖

interpreter1.png

  • 一個解釋器模式中包含的四種角色html

    • 抽象(或接口)解釋器(Interpreter):聲明一個全部具體表達式都要實現的抽象接口(或者抽象類),接口中主要是一個interpret()方法,稱爲解釋操做。具體解釋任務由它的各個實現類來完成,具體的解釋器分別由終結符解釋器和非終結符解釋器完成。
    • 終結符表達式(TerminalExpression):實現與文法中的元素相關聯的解釋操做,一般一個解釋器模式中只有一個終結符表達式,但有多個實例,對應不一樣的終結符。終結符一半是文法中的運算單元,好比有一個簡單的公式R=R1+R2,在裏面R1和R2就是終結符,對應的解析R1和R2的解釋器就是終結符表達式。
    • 非終結符表達式(NonterminalExpression):文法中的每條規則對應於一個非終結符表達式,非終結符表達式通常是文法中的運算符或者其餘關鍵字,好比公式R=R1+R2中,+就是非終結符,解析+的解釋器就是一個非終結符表達式。非終結符表達式根據邏輯的複雜程度而增長,原則上每一個文法規則都對應一個非終結符表達式。
    • 環境角色(Context):這個角色的任務通常是用來存放文法中各個終結符所對應的具體值,好比R=R1+R2,咱們給R1賦值100,給R2賦值200。這些信息須要存放到環境角色中,不少狀況下咱們使用Map來充當環境角色就足夠了。

0x02.適用場景

  • 在某個特定類型問題發生頻率足夠高,須要自定義語法的場景。好比數據按照配置規則ETL!

0x03.優缺點

1.優勢

  • 語法由不少類表示,容易改變及擴展此「語言」

2.缺點

  • 當語法規則數目太多時,增長了系統複雜度。

0x04.樣例代碼

使用解釋器模式實現一個簡單的語法 計算 6 100 11 + * 表達式,首先記錄數值,而後按照順序添加符號計算。
100 + 11
111 * 6
666
  • java代碼實現
/**
 * 表達式定義接口
 */
public interface Interpreter {
    int interpret();
}

/**
 * 相加表達式
 */
public class AddInterpreter implements Interpreter {

    private Interpreter firstExpression, secondExpression;

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

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

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

/**
 * 相乘規則表達式
 */
public class MultiInterpreter implements Interpreter {

    private Interpreter firstExpression, secondExpression;

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


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

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

/**
 * 數值型表達式
 */
public class NumberInterpreter implements Interpreter {

    private int number;

    public NumberInterpreter(int number) {
        this.number = number;
    }

    public NumberInterpreter(String number){
        this.number = Integer.parseInt(number);
    }

    @Override
    public int interpret() {
        return this.number;
    }
}

/**
 * 格式化表達式
 */
public class ExpressionParser {

    private Stack<Interpreter> stack = new Stack<>();

    public int parse (String expression) {
        String[] itemArray = expression.split(" ");
        for (String symbol : itemArray) {
            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.getExpression(firstExpression, secondExpression, symbol);
                System.out.println(String.format("應用運算符: %s", operator));
                int result = operator.interpret();
                NumberInterpreter numberInterpreter = new NumberInterpreter(result);
                stack.push(numberInterpreter);
                System.out.println(String.format("階段結果入棧: %d", result));
            }
        }
        return stack.pop().interpret();
    }
}

/**
 * 工具類
 */
public class OperatorUtil {

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

    public static Interpreter getExpression(Interpreter first,
                                            Interpreter second,
                                            String symbol) {
        switch (symbol) {
            case "+":
                return new AddInterpreter(first, second);
            case "*":
                return new MultiInterpreter(first, second);
            default:
                return null;
        }
    }
}
  • 測試與應用
/**
 * 測試類
 */
public class Test {

    public static void main(String[] args) {
        String input = "6 100 11 + *";
        ExpressionParser parser = new ExpressionParser();
        int result = parser.parse(input);
        System.out.println("解釋器運算結果:" + result);
    }
}
  • 輸出日誌
入棧: 6
入棧: 100
入棧: 11
出棧:11 和 100
應用運算符: +
階段結果入棧: 111
出棧:111 和 6
應用運算符: *
階段結果入棧: 666
解釋器運算結果:666
  • 代碼對應的UML類圖

interpreter2.png

在樣例中:AddInterpreter和MultiInterpreter爲終結表達式,NumberInterpreter爲非終結表達式,ExpressionParser爲環境角色。java

0x05.相關設計模式

  • 解釋器模式和適配器模式git

    • 適配器模式不須要先知道適配的規則
    • 解釋器模式要預先知道語法規則

0x06.源碼中的解釋器模式

  • Pattern
  • Spring SpelExpressionParse

0x07.代碼地址

0x08.推薦閱讀

相關文章
相關標籤/搜索