解釋器模式

定義

解釋器(Interpreter)模式給分析對象定義一個語言,並定義該語言的文法表示,再設計一個解析器來解釋語言中的句子。也就是說,用編譯語言的方式來分析應用中的實例。這種模式實現了文法表達式處理的接口,該接口解釋一個特定的上下文。

解釋器模式屬於類行爲型模式。


要點

優點:

  1. 擴展性好。由於在解釋器模式中使用類來表示語言的文法規則,因此可以通過繼承等機制來改變或擴展文法。
  2. 容易實現。在語法樹中的每個表達式節點類都是相似的,所以實現其文法較爲容易。

缺點:

  1. 執行效率較低。解釋器模式中通常使用大量的循環和遞歸調用,當要解釋的句子較複雜時,其運行速度很慢,且代碼的調試過程也比較麻煩。
  2. 會引起類膨脹。解釋器模式中的每條規則至少需要定義一個類,當包含的文法規則很多時,類的個數將急劇增加,導致系統難以管理與維護。
  3. 可應用的場景比較少。在軟件開發中,需要定義語言文法的應用實例非常少,所以這種模式很少被使用到。

主要角色:
抽象表達式(Abstract Expression):定義解釋器的接口,約定解釋器的解釋操作,主要包含解釋方法 interpret()。
終結符表達式(Terminal Expression):是抽象表達式的子類,用來實現文法中與終結符相關的操作,文法中的每一個終結符都有一個具體終結表達式與之相對應。
非終結符表達式(Nonterminal Expression):也是抽象表達式的子類,用來實現文法中與非終結符相關的操作,文法中的每條規則都對應於一個非終結符表達式。
環境(Context):通常包含各個解釋器需要的數據或是公共的功能,一般用來傳遞被所有解釋器共享的數據,後面的解釋器可以從這裏獲取這些值。

在這裏插入圖片描述


場景

解釋器模式實現的關鍵是定義文法規則、設計終結符類與非終結符類、畫出結構圖,必要時構建語法樹。

假如“京津冀”公交車讀卡器可以判斷乘客的身份,如果是【北京】或者【天津】的 “老人” 、 “兒童” 就可以免費乘車,其他人員乘車一次扣 1 元。

實現

/**
 * 抽象表達式類
 */
interface Expression {

    boolean interpret(String info);

}

/**
 * 終結符表達式類
 */
class TerminalExpression implements Expression {

    private Set<String> set = new HashSet<>();

    public TerminalExpression(String[] data) {
        Collections.addAll(set, data);
    }

    @Override
    public boolean interpret(String info) {
        return set.contains(info);
    }

}

/**
 * 非終結符表達式類
 */
class AndExpression implements Expression {

    private Expression city;
    private Expression person;

    public AndExpression(Expression city, Expression person) {
        this.city = city;
        this.person = person;
    }

    @Override
    public boolean interpret(String info) {
        String[] s = info.split("的");
        return city.interpret(s[0]) && person.interpret(s[1]);
    }

}


/**
 * 環境類
 */
class Context {

    private String[] citys = {"北京", "天津"};
    private String[] persons = {"老人", "兒童"};
    private Expression cityPerson;

    public Context() {
        Expression city = new TerminalExpression(citys);
        Expression person = new TerminalExpression(persons);
        cityPerson = new AndExpression(city, person);
    }

    public void freeRide(String info) {
        boolean ok = cityPerson.interpret(info);
        if (ok) {
            System.out.println("您是" + info + ",您本次乘車免費!");
        } else {
            System.out.println(info + ",您不是免費人員,本次乘車扣費1元!");
        }
    }
}
public class Client {

    /**
     * 文法規則
     * <expression> ::= <city>的<person>
     * <city> ::= 北京 | 天津
     * <person> ::= 老人 | 兒童
     */
    public static void main(String[] args) {
        Context bus = new Context();

        bus.freeRide("北京的老人");
        bus.freeRide("北京的中年人");
        bus.freeRide("天津的婦女");
        bus.freeRide("天津的兒童");
        bus.freeRide("河北的中年人");
    }

}

----------------輸出----------------
您是北京的老人,您本次乘車免費!
北京的年輕人,您不是免費人員,本次乘車扣費1元!
天津的婦女,您不是免費人員,本次乘車扣費1元!
您是天津的兒童,您本次乘車免費!
河北的年輕人,您不是免費人員,本次乘車扣費1元!

源碼

Click here

總結

適用場景:

  1. 當語言的文法較爲簡單,且執行效率不是關鍵問題時。
  2. 當問題重複出現,且可以用一種簡單的語言來進行表達時。
  3. 當一個語言需要解釋執行,並且語言中的句子可以表示爲一個抽象語法樹的時候,如 XML 文檔解釋。

解釋器模式在實際的軟件開發中使用比較少,因爲它會引起效率、性能以及維護等問題。如果碰到對表達式的解釋,在 Java 中可以用 Expression4J 或 Jep 等來設計。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章