1. 程式人生 > 實用技巧 >設計模式之直譯器模式

設計模式之直譯器模式

設計模式之直譯器模式

直譯器模式:是一種按照規定語法進行解析的方案,在現在專案中使用較少 ,給定一門語言,定義它的規則的一種表示式,並定義一個直譯器,該直譯器使用該表示式來解釋語言中的句子。

用的比較少,瞭解即可

2.直譯器模式例子:

  例子:輸入一個模型公式(加、減運算),然後輸入模型中的引數,運算出結果。

3.程式碼

3.1 建立一個Expression表示式類

public abstract class Expression {

    /**
     * 抽象表示式,用來解析表示式的值
     * map的key表示具體的變數。value表示變數的值
     *
     * @param map
     * @return
     */
    public abstract int interpreter(Map<String, Integer> map);
}

3.2 建立一個變數解析器  

public class VarExpression extends Expression {

    private String key;

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

    // 獲取具體變數的值
    @Override
    public int interpreter(Map<String, Integer> map) {
        return map.get(key);
    }
}

3.3 抽象運算子號解析器

public abstract class SymbolExpression extends Expression {

    protected Expression left;

    protected Expression right;

    public SymbolExpression(Expression left, Expression right) {
        System.out.println("呼叫了symbolExpression中的方法=========>left  " + left + "=========>" + "right:  " + right + "  ");
        this.left = left;
        this.right = right;
    }
}

3.4 加法解析器

public class AddExpression extends SymbolExpression {

    public AddExpression(Expression left, Expression right) {
        super(left, right);
    }

    @Override
    public int interpreter(Map<String, Integer> map) {
        int result = super.left.interpreter(map) + super.right.interpreter(map);
        System.out.println("   add中的result============>" + result + "  ");
        return result;
    }
}

3.5 減法解析器

public class SubExpression extends SymbolExpression {

    public SubExpression(Expression left, Expression right) {
        super(left, right);
    }

    @Override
    public int interpreter(Map<String, Integer> map) {
        int subresult = super.left.interpreter(map) - super.right.interpreter(map);
        System.out.println("   sub計算結果=======>" + subresult + "   ");
        return subresult;
    }
}

3.6 建立解析器封裝器

public class Calculator {

    private Expression expression;

    public Calculator(String expStr) {
        // 定義一個棧,安排執行的先後執行
        Stack<Expression> stack = new Stack<>();
        // 表示式拆分為位元組資料
        char[] charArray = expStr.toCharArray();

        Expression left;
        Expression right;
        for (int i = 0; i < charArray.length; i++) {
            switch (charArray[i]) {
                case '+':
                    left = stack.pop();
                    right = new VarExpression(String.valueOf(charArray[++i]));
                    stack.push(new AddExpression(left, right));
                    System.out.println("left: " + left + "  right==>" + right + "  stack中的值======>" + stack.toString());
                    break;
                case '-':
                    left = stack.pop();
                    right = new VarExpression(String.valueOf(charArray[++i]));
                    stack.push(new SubExpression(left, right));
                    System.out.println("left: " + left + "  right==>" + right + "  stack中的值======>" + stack.toString());
                    break;
                default:
                    // 公式中的變數
                    stack.push(new VarExpression(String.valueOf(charArray[i])));
                    System.out.println("stack中的值======>" + stack.toString());
            }
        }
        // 把運算結果丟擲來
        this.expression = stack.pop();
    }

    // 開始運算
    public int run(HashMap<String, Integer> map) {
        return this.expression.interpreter(map);
    }
}

3.7 客戶端程式碼

public class Client {

    public static void main(String[] args) throws Exception {
        String expStr = getExpStr();
        Calculator calculator = new Calculator(expStr);
        // 賦值
        HashMap<String, Integer> var = getVar(expStr);
        System.out.println("運算結果為:" + expStr + "=" + calculator.run(var));
    }

    private static HashMap<String, Integer> getVar(String expStr) throws Exception {

        HashMap<String, Integer> hashMap = new HashMap<String, Integer>();
        // 解析幾個引數要傳遞
        for (char ch : expStr.toCharArray()) {
            if (ch != '+' && ch != '-') {
                // 解決重複引數的問題
                if (!hashMap.containsKey(String.valueOf(ch))) {
                    System.out.println("請輸入變數" + ch + "的值: ");
                    String in = (new BufferedReader(new InputStreamReader(System.in))).readLine();
                    hashMap.put(String.valueOf(ch), Integer.valueOf(in));
                }
            }
        }
        return hashMap;
    }

    private static String getExpStr() throws IOException {
        System.out.println("請輸入表示式");
        return (new BufferedReader(new InputStreamReader(System.in))).readLine();
    }
}

執行截圖:

4.直譯器的優點:

  直譯器是一個簡單語法分析工具,它最顯著的優點就是擴充套件性,修改語法規則只要修改相應的表示式就可以了,若擴充套件語法,則只要增加規則類就可以了,例如add,sub。

5.直譯器的缺點

  • 直譯器模式會引起類膨脹:一個規則加一個類
  • 直譯器模式採用遞迴呼叫方法:如果程式報錯了,一步步除錯下去,非常複雜
  • 效率問題:直譯器模式由於使用了大量的迴圈和遞迴,效率是一個不容忽視的問題,特別是一用於解析複雜、冗長的語法時,效率是難以忍受的。

6.直譯器的應用場景

  • 重複發生的問題可以使用直譯器模式
  • 一個簡單語法需要解釋的場景

7.直譯器模式注意事項

  儘量不要在重要的模組中使用直譯器模式,否則維護會是一個很大的問題。在專案中可以使用shell、JRuby、Groovy等指令碼語言來代替直譯器模式,彌補Java編譯型語言的不足。我所在公司是金融證券公司,涉及到很多業務型別的計算場景。我們的計算元件採用的時候MVEL表示式的方式進行計算,使用起來美滋滋的。準備使用直譯器模式的時候,可以百度搜一下Expression4J、MESP(Math Expression String Parser)、Jep等開源的解析工具包。功能都異常強大,而且非常容易使用,效率也還不錯,實現大多數的數學運算完全沒有問題。總之,就是有大佬開發的現成的工具,直接拿來用就好了