行為型設計模式 – 解釋器模式詳解

基本介紹

解釋器模式(Interpreter Pattern)提供了評估語言的語法或表達式的方式,它屬於行為型模式。這種模式實現了一個表達式介面,該介面解釋一個特定的上下文。

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

該模式對於複雜的場景實現起來比較困難,實際應用較少,大家了解即可。

模式結構

Context(環境角色):聲明一個所有具體表達式都要實現的抽象介面(或者抽象類),介面中主要是一個interpret() 方法,稱為解釋操作。具體解釋任務由它的各個實現類來完成,具體的解釋器分別由終結符解釋器 TerminalExpression 和非終結符解釋器 NonterminalExpression 完成。

AbstractExpression(抽象解釋器):實現與文法中的元素相關聯的解釋操作,通常一個解釋器模式中只有一個終結符表達式,但有多個實例,對應不同的終結符。終結符一半是文法中的運算單元,比如有一個簡單的公式R=R1+R2,在裡面 R1 和 R2 就是終結符,對應的解析 R1 和 R2 的解釋器就是終結符表達式。

TerminalExpression(終結符表達式):文法中的每條規則對應於一個非終結符表達式,非終結符表達式一般是文法中的運算符或者其他關鍵字,比如公式 R=R1+R2 中,+ 就是非終結符,解析 + 的解釋器就是一個非終結符表達式。非終結符表達式根據邏輯的複雜程度而增加,原則上每個文法規則都對應一個非終結符表達式。

NoterminalExpression(非終結符表達式):這個角色的任務一般是用來存放文法中各個終結符所對應的具體值,比如 R=R1+R2,我們給 R1 賦值 100,給 R2 賦值 200。這些資訊需要存放到環境角色中,很多情況下我們使用 Map 來充當環境角色就足夠了。

舉例說明

使用解釋器模式實現數字的加減法

1、抽象解釋器

/**
 * 抽象解釋器
 */
public abstract class AbstractExpression {
    public abstract int interpret(Context context);
}

2、非終結符表達式

/**
 * 非終結表達式:加法
 */
public class AddExpression extends AbstractExpression {
    private final AbstractExpression left;
    private final AbstractExpression right;

    public AddExpression(AbstractExpression left, AbstractExpression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpret(Context context) {
        return left.interpret(context) + right.interpret(context);
    }
}

/**
 * 非終結表達式:減法
 */
public class SubExpression extends AbstractExpression {
    private final AbstractExpression left;
    private final AbstractExpression right;

    public SubExpression(AbstractExpression left, AbstractExpression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public int interpret(Context context) {
        return left.interpret(context) - right.interpret(context);
    }
}

3、終結表達式

/**
 * 終結表達式:變數
 */
public class Variable extends AbstractExpression {
    private final String key;

    public Variable(String key) {
        this.key = key;
    }

    @Override
    public int interpret(Context context) {
        return context.getValue(key);
    }
}

4、環境角色

/**
 * 環境上下文
 */
public class Context {
    private final Map<String, Integer> valueMap = new HashMap<>();

    public void addValue(final String key, final int value) {
        valueMap.put(key, value);
    }

    public int getValue(final String key) {
        return valueMap.get(key);
    }

    public Map<String, Integer> getValueMap() {
        return valueMap;
    }
}

5、測試類

public class Client {
    @Test
    public void test(){
        Context context = new Context();

        context.addValue("a", 6);
        context.addValue("b", 9);
        context.addValue("c", 1);

        Variable a = new Variable("a");
        Variable b = new Variable("b");
        Variable c = new Variable("c");

        AbstractExpression addValue = new AddExpression(a, b);
        AbstractExpression subValue = new SubExpression(addValue, c);

        System.out.println(context.getValueMap());
        System.out.println("a + b - c = " + subValue.interpret(context));
    }
}

6、運行結果

{a=6, b=9, c=1}
a+b-c=14

模式分析

優點:

  • 可擴展性比較好,靈活
  • 增加了新的解釋表達式的方式
  • 易於實現簡單文法

缺點:

  • 可利用場景比較少
  • 對於複雜的文法比較難維護
  • 解釋器模式會引起類膨脹
  • 解釋器模式採用遞歸調用方法

適用場景:

  • 有一個簡單的語法規則,比如一個 sql 語句,如果我們需要根據 sql 語句進行 rm 轉換,就可以使用解釋器模式來對語句進行解釋。
  • 一些重複發生的問題,比如加減乘除四則運算,但是公式每次都不同,有時是 a+b-cd,有時是 ab+c-d,等等等等個,公式千變萬化,但是都是由加減乘除四個非終結符來連接的,這時我們就可以使用解釋器模式。

參考