Android 架構師之路12 設計模式之解釋器模式

Android 架構師之路 目錄

1、解釋器模式概念

1.1 介紹

解釋器模式(Interpreter Pattern)提供了評估語言的語法或表達式的方式,它屬于行為型模式。這種模式實現了一個表達式接口,該接口解釋一個特定的上下文。這種模式被用在 SQL 解析、符號處理引擎等。

1.2 定義

解釋器模式是類的行為模式。給定一個語言之后,解釋器模式可以定義出其文法的一種表示,并同時提供一個解釋器。客戶端可以使用這個解釋器來解釋這個語言中的句子。

  • 文法:即語法規則。在解釋器模式中每一個語法都將對應一個解釋器對象,用來處理相應的語法規則。它對于擴展、改變文法以及增加新的文法規則都很方便。
  • 解釋器模式描述了如何為簡單的語言定義一個文法,如何在該語言中表示一個句子,以及如何解釋這些句子。
  • 在解釋器模式中可以通過一種稱之為抽象語法樹(Abstract Syntax Tree, AST)的圖形方式來直觀地表示語言的構成,每一棵抽象語法樹對應一個語言實例
1.3 使用場景
  • 有多個子類共有的方法,且邏輯相同。
  • 重要的、復雜的方法,可以考慮作為模板方法。

2、命令模式UML類圖

解釋器模式
  • AbstractExpression:定義解釋器的接口,約定解釋器的解釋操作。其中的Interpret接口,正如其名字那樣,它是專門用來解釋該解釋器所要實現的功能。(如加法解釋器中的Interpret接口就是完成兩個操作數的相加功能)。

  • TerminalExpression:終結符解釋器,用來實現語法規則中和終結符相關的操作,不再包含其他的解釋器,如果用組合模式來構建抽象語法樹的話,就相當于組合模式中的葉子對象,可以有多種終結符解釋器。

  • NonterminalExpression:非終結符解釋器,用來實現語法規則中非終結符相關的操作,通常一個解釋器對應一個語法規則,可以包含其他解釋器,如果用組合模式構建抽象語法樹的話,就相當于組合模式中的組合對象。可以有多種非終結符解釋器。

  • Context:上下文,通常包含各個解釋器需要的數據或是公共的功能。這個Context在解釋器模式中起著非常重要的作用。一般用來傳遞被所有解釋器共享的數據,后面的解釋器可以從這里獲取這些值。

  • Client:客戶端,指的是使用解釋器的客戶端,通常在這里將按照語言的語法做的表達式轉換成使用解釋器對象描述的抽象語法樹,然后調用解釋操作。

3、命令模式代碼實現

加減算法

AbstractExpression:
public abstract class Expression {
    /**
     * 以環境為準,本方法解釋給定的任何一個表達式
     */
    public abstract int interpret(Context ctx);
    /**
     * 檢驗兩個表達式在結構上是否相同
     */
    public abstract boolean equals(Object obj);
    /**
     * 返回表達式的hash code
     */
    public abstract int hashCode();
    /**
     * 將表達式轉換成字符串
     */
    public abstract String toString();
}
NonterminalExpression:
public class Minus extends Expression {

    private Expression left, right;

    public Minus(Expression left, Expression right) {
        this.left = left;
        this.right = right;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj != null && obj instanceof Minus) {
            return left.equals(((Minus) obj).left) && right.equals(((Minus) obj).right);
        }
        return false;
    }

    @Override
    public int hashCode() {
        return this.toString().hashCode();
    }

    @Override
    public int interpret(Context ctx) {

        return left.interpret(ctx) - right.interpret(ctx);
    }

    @Override
    public String toString() {
        return "(" + left.toString() + " - " + right.toString() + ")";
    }
}

public class Plus extends Expression {

    private Expression left,right;

    public Plus(Expression left , Expression right){
        this.left = left;
        this.right = right;
    }
    @Override
    public boolean equals(Object obj) {
        if(obj != null && obj instanceof Plus)
        {
            return left.equals(((Plus)obj).left) &&
                    right.equals(((Plus)obj).right);
        }
        return false;
    }

    @Override
    public int hashCode() {
        return this.toString().hashCode();
    }

    @Override
    public int interpret(Context ctx) {

        return left.interpret(ctx) + right.interpret(ctx);
    }

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

}
TerminalExpression:
public class Variable extends Expression {

    private String name;

    public Variable(String name){
        this.name = name;
    }
    @Override
    public boolean equals(Object obj) {

        if(obj != null && obj instanceof Variable)
        {
            return this.name.equals(
                    ((Variable)obj).name);
        }
        return false;
    }

    @Override
    public int hashCode() {
        return this.toString().hashCode();
    }

    @Override
    public String toString() {
        return name;
    }

    @Override
    public int interpret(Context ctx) {
        return ctx.lookup(this);
    }

}

public class Constant extends Expression{

    private int value;

    public Constant(int value){
        this.value = value;
    }

    @Override
    public boolean equals(Object obj) {

        if(obj != null && obj instanceof Constant){
            return this.value == ((Constant)obj).value;
        }
        return false;
    }

    @Override
    public int hashCode() {
        return this.toString().hashCode();
    }

    @Override
    public int interpret(Context ctx) {

        return value;
    }

    @Override
    public String toString() {
        return new Integer(value).toString();
    }

}
Receiver:
public class Receiver implements Serializable {

    public void onLeft() {
        System.out.println("向左");
    }

    public void onRight() {
        System.out.println("向右");
    }

    public void onBottom() {
        System.out.println("向下");
    }

    public void onTransformation() {
        System.out.println("變形");
    }
}
Context:
public class Context {

    private Map<Variable,Integer> map = new HashMap<Variable,Integer>();

    public void assign(Variable var , int value){
        map.put(var, new Integer(value));
    }

    public int lookup(Variable var) throws IllegalArgumentException{
        Integer value = map.get(var);
        if(value == null){
            throw new IllegalArgumentException();
        }
        return value.intValue();
    }
}

Client :
public class Client {

    public static void main(String[] args) {
        Context ctx = new Context();
        Variable x = new Variable("x");
        Variable y = new Variable("y");
        Constant c = new Constant(1);
        ctx.assign(x, 2);
        ctx.assign(y, 3);
        Expression exp = new Plus(new Plus(c,x) , new Minus(y,x));

        System.out.println(exp.toString() + "=" + exp.interpret(ctx));
    }

}

結果輸出:

((1 + x) + (y - x))=4

4、模式總結

4.1 優點
  • 易于改變和擴展文法。

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

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

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

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

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,763評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,238評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,823評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,604評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,339評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,713評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,712評論 3 445
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,893評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,448評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,201評論 3 357
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,397評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,944評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,631評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,033評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,321評論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,128評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,347評論 2 377

推薦閱讀更多精彩內容

  • 1 場景問題# 1.1 讀取配置文件## 考慮這樣一個實際的應用,維護系統自定義的配置文件。 幾乎每個實際的應用系...
    七寸知架構閱讀 3,131評論 2 56
  • 設計模式匯總 一、基礎知識 1. 設計模式概述 定義:設計模式(Design Pattern)是一套被反復使用、多...
    MinoyJet閱讀 3,961評論 1 15
  • 解釋器模式在我看來,應用范圍很小,我想了很久,想舉一個不是加減乘除運算的例子出來,但是很難......在設計模式一...
    Mock2052閱讀 965評論 0 1
  • 作業1 被公舉為“群花”不是沒有道理的:深陷的眼窩里嵌著一雙熟透的黑玫瑰葡萄似的眼睛,雖不長卻自然上挑的如黛的眉毛...
    同處當下閱讀 259評論 3 0
  • 今天,早上爸爸來奶奶家接我,我爺爺說我不吃飯,然后叫我爸爸帶我去醫院檢查一下。在醫院里我抽了血,我感覺一點也不疼,...
    楊東峻666閱讀 194評論 0 0