1. 程式人生 > >java設計模式——直譯器模式(Interpreter Pattern)

java設計模式——直譯器模式(Interpreter Pattern)

概述:       直譯器模式是一種使用頻率相對較低但學習難度較大的設計模式,它用於描述如何使用面嚮物件語言構成一個簡單的語言直譯器。在某些情況下,為了更好地描述某一些特定型別的問題,我們可以建立一種新的語言,這種語言擁有自己的表示式和結構,即文法規則,這些問題的例項將對應為該語言中的句子。此時,可以使用直譯器模式來設計這種新的語言。對直譯器模式的學習能夠加深我們對面向物件思想的理解,並且掌握程式語言中文法規則的解釋過程。
定義:        直譯器模式(Interpreter Pattern):定義一個語言的文法,並且建立一個直譯器來解釋該語言中的句子,這裡的“語言”是指使用規定格式和語法的程式碼。直譯器模式是一種類行為型模式。

結構:
  • Expression(抽象表示式):在抽象表示式中聲明瞭抽象的解釋操作,它是所有終結符表示式和非終結符表示式的公共父類。
  • TerminalExpression(終結符表示式):終結符表示式是抽象表示式的子類,它實現了與文法中的終結符相關聯的解釋操作,在句子中的每一個終結符都是該類的一個例項。通常在一個直譯器模式中只有少數幾個終結符表示式類,它們的例項可以通過非終結符表示式組成較為複雜的句子。
  • NonterminalExpression(非終結符表示式):非終結符表示式也是抽象表示式的子類,它實現了文法中非終結符的解釋操作,由於在非終結符表示式中可以包含終結符表示式,也可以繼續包含非終結符表示式,因此其解釋操作一般通過遞迴的方式來完成。
  • Context(環境類):環境類又稱為上下文類,它用於儲存直譯器之外的一些全域性資訊,通常它臨時儲存了需要解釋的語句。

UML圖:


場景:比如計算長方形面積時,有相關的公式進行計算,我們只需要輸入對應的引數,對應的結果會由系統計算出來。
程式碼分析: /**
 * Created by yangjun on 2016/12/20.
 */

public abstract class 
Expression {

    /**
     *  解析公式和數值 , 其中 var 中的 key 值是是公式中的引數, value 值是具體的數字
      @param  var  該引數相當於 Context
     *  @return
      */
    public abstract int interpreter(HashMap<String,Integer> var);
}

/**
 * Created by yangjun on 2016/12/20.
 *  終結符類
  */

public class VarExpression  extends Expression {
    private String  m_key;
    public VarExpression(String key){
        this. m_key = key;
    }

    @Override
    public int interpreter(HashMap<String, Integer> var) {
        return var.get( m_key);
    }
}

/**
 * Created by yangjun on 2016/12/20.
 *  符號類
  */

public abstract class SymbolExpression  extends Expression {
    protected Expression  m_left;
    protected Expression  m_right;
    public SymbolExpression(Expression left,Expression right){
        this. m_left = left;
        this. m_right = right;
    }
}

/**
 * Created by yangjun on 2016/12/20.
 *  乘法直譯器(非終結符類)
  */

public class MultiplyExpression  extends SymbolExpression {
    public MultiplyExpression(Expression left, Expression right) {
        super(left, right);
    }

    @Override
    public int interpreter(HashMap<String, Integer> var) {
        return super. m_left.interpreter(var) *  super. m_right.interpreter(var);
    }
}

/**
 * Created by yangjun on 2016/12/20.
 *  直譯器封裝類
  */

public class Calculater {

    private Expression  expression;

    public void build(String strExp){
        Expression left =  null;
        Expression right =  null;
        Stack<Expression> stack =  new Stack<Expression>();
        char[] chars = strExp.toCharArray();
        for ( int i =  0; i < chars. length; i++) {
            switch (chars[i]){
                case  '+':
                    left = stack.pop();
                    right =  new VarExpression(String. valueOf(chars[++i]));
                    stack.push( new AddExpression(left,right));
                    break;
                case  '-':
                    left = stack.pop();
                    right =  new VarExpression(String. valueOf(chars[++i]));
                    stack.push( new SubExpression(left,right));
                    break;
                case  '*':
                    left = stack.pop();
                    right =  new VarExpression(String. valueOf(chars[++i]));
                    stack.push( new MultiplyExpression(left,right));
                    break;
                case  '/':
                    left = stack.pop();
                    right =  new VarExpression(String. valueOf(chars[++i]));
                    stack.push( new DivisionExpression(left,right));
                    break;
                default:
                    stack.push( new VarExpression(String. valueOf(chars[i])));
            }
        }
        this. expression = stack.pop();
    }
    public int compute(HashMap<String, Integer> var){
        return  expression.interpreter(var);
    }

}

客戶端呼叫:     String strExp =  "a*b";
    Calculater calculater =  new Calculater();
    calculater.build(strExp);
    int compute = calculater.compute(rectangularArea( 2, 3));
    Log. d( "test", "compute = "+compute);
}

/**
 *  長方形面積
  @param  lenght
  @param  with
  @return
  */
public HashMap<String,Integer> rectangularArea( int lenght, int with){
    HashMap<String,Integer> hashMap =  new HashMap<String,Integer>();
    hashMap.put( "a",lenght);
    hashMap.put( "b",with);
    return hashMap;
}

log輸出: 12-20 16:21:23.253 20582-20582/? D/test: compute = 6
優點:
  • 易於改變和擴充套件文法。由於在直譯器模式中使用類來表示語言的文法規則,因此可以通過繼承等機制來改變或擴充套件文法。
  • 每一條文法規則都可以表示為一個類,因此可以方便地實現一個簡單的語言。
  • 實現文法較為容易。在抽象語法樹中每一個表示式節點類的實現方式都是相似的,這些類的程式碼編寫都不會特別複雜,還可以通過一些工具自動生成節點類程式碼。
  • 增加新的解釋表示式較為方便。如果使用者需要增加新的解釋表示式只需要對應增加一個新的終結符表示式或非終結符表示式類,原有表示式類程式碼無須修改,符合“開閉原則”。

缺點:
  • 對於複雜文法難以維護。在直譯器模式中,每一條規則至少需要定義一個類,因此如果一個語言包含太多文法規則,類的個數將會急劇增加,導致系統難以管理和維護,此時可以考慮使用語法分析程式等方式來取代直譯器模式。
  • 執行效率較低。由於在直譯器模式中使用了大量的迴圈和遞迴呼叫,因此在解釋較為複雜的句子時其速度很慢,而且程式碼的除錯過程也比較麻煩。

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