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

設計模式之十九之直譯器模式

基本介紹

1) 在編譯原理中,一個算術表示式通過詞法分析器形成詞法單元,而後這些詞法 單元再通過語法分析器構建語法分析樹,最終形成一顆抽象的語法分析樹。這 裡的詞法分析器和語法分析器都可以看做是直譯器

2) 直譯器模式(Interpreter Pattern):是指給定一個語言(表示式),定義它的文法 的一種表示,並定義一個直譯器,使用該直譯器來解釋語言中的句子(表示式)

3) 應用場景 • 應用可以將一個需要解釋執行的語言中的句子表示為一個抽象語法樹 • 一些重複出現的問題可以用一種簡單的語言來表達 • 一個簡單語法需要解釋的場景

4) 這樣的例子還有,比如編譯器、運算表示式計算、正則表示式、機器人等

uml類圖:

對原理類圖的說明-即(直譯器模式的角色及職責)

1) Context: 是環境角色,含有直譯器之外的全域性資訊.

2) AbstractExpression: 抽象表示式, 宣告一個抽象的解釋操作,這個方法為抽象語法樹中所有的節點所 共享

3) TerminalExpression: 為終結符表示式, 實現與文法中的終結符相關的解釋操作

4) NonTermialExpression: 為非終結符表示式,為文法中的非終結符實現解釋操作.

5) 說明: 輸入Context he TerminalExpression 資訊通過Client 輸入即可

案例:

1) 應用例項要求 通過直譯器模式來實現四則運算, 如計算a+b-c的值

uml類圖

程式碼實現:

package com.hy.interpreter;

import java.util.HashMap;

/**
 * 抽象類表示式,通過HashMap 鍵值對, 可以獲取到變數的值
 *
 * @author hanyong
 * @date 2020/12/10 22:59
 */
public abstract class Expression {
    // a + b - c
    // 解釋公式和數值, key 就是公式(表示式) 引數[a,b,c], value就是就是具體值
    // HashMap {a=10, b=20}
    public abstract
int interpreter(HashMap<String, Integer> var); } package com.hy.interpreter; import java.util.HashMap; /** * 變數的直譯器 * * @author hanyong * @date 2020/12/10 23:00 */ public class VarExpression extends Expression { private String key; // key=a,key=b,key=c public VarExpression(String key) { this.key = key; } // var 就是{a=10, b=20} // interpreter 根據 變數名稱,返回對應值 @Override public int interpreter(HashMap<String, Integer> var) { return var.get(this.key); } } package com.hy.interpreter; import java.util.HashMap; /** * 抽象運算子號解析器 這裡,每個運算子號,都只和自己左右兩個數字有關係, * 但左右兩個數字有可能也是一個解析的結果,無論何種型別,都是Expression類的實現類 * * @author hanyong * @date 2020/12/10 23:02 */ public class SymbolExpression extends Expression { protected Expression left; protected Expression right; public SymbolExpression(Expression left, Expression right) { this.left = left; this.right = right; } //因為 SymbolExpression 是讓其子類來實現,因此 interpreter 是一個預設實現 @Override public int interpreter(HashMap<String, Integer> var) { // TODO Auto-generated method stub return 0; } } package com.hy.interpreter; import java.util.HashMap; /** * @author hanyong * @date 2020/12/10 23:03 */ public class SubExpression extends SymbolExpression { public SubExpression(Expression left, Expression right) { super(left, right); } //求出left 和 right 表示式相減後的結果 @Override public int interpreter(HashMap<String, Integer> var) { return super.left.interpreter(var) - super.right.interpreter(var); } } package com.hy.interpreter; import java.util.HashMap; /** * 加法直譯器 * * @author hanyong * @date 2020/12/10 23:04 */ public class AddExpression extends SymbolExpression { public AddExpression(Expression left, Expression right) { super(left, right); } //處理相加 //var 仍然是 {a=10,b=20}.. //一會我們debug 原始碼,就ok @Override public int interpreter(HashMap<String, Integer> var) { //super.left.interpreter(var) : 返回 left 表示式對應的值 a = 10 //super.right.interpreter(var): 返回right 表示式對應值 b = 20 return super.left.interpreter(var) + super.right.interpreter(var); } } package com.hy.interpreter; import java.util.HashMap; import java.util.Stack; /** * @author hanyong * @date 2020/12/10 23:05 */ public class Calculator { // 定義表示式 private Expression expression; // 建構函式傳參,並解析 public Calculator(String expStr) { // expStr = a+b // 安排運算先後順序 Stack<Expression> stack = new Stack<>(); // 表示式拆分成字元陣列 char[] charArray = expStr.toCharArray();// [a, +, b] Expression left = null; Expression right = null; //遍歷我們的字元陣列, 即遍歷 [a, +, b] //針對不同的情況,做處理 for (int i = 0; i < charArray.length; i++) { switch (charArray[i]) { case '+': // left = stack.pop();// 從stack取出left => "a" right = new VarExpression(String.valueOf(charArray[++i]));// 取出右表示式 "b" stack.push(new AddExpression(left, right));// 然後根據得到left 和 right 構建 AddExpresson加入stack break; case '-': // left = stack.pop(); right = new VarExpression(String.valueOf(charArray[++i])); stack.push(new SubExpression(left, right)); break; default: //如果是一個 Var 就建立要給 VarExpression 物件,並push到 stack stack.push(new VarExpression(String.valueOf(charArray[i]))); break; } } //當遍歷完整個 charArray 陣列後,stack 就得到最後Expression this.expression = stack.pop(); } public int run(HashMap<String, Integer> var) { //最後將表示式a+b和 var = {a=10,b=20} //然後傳遞給expression的interpreter進行解釋執行 return this.expression.interpreter(var); } } package com.hy.interpreter; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.HashMap; /** * @author hanyong * @date 2020/12/10 23:16 */ public class ClientTest { public static void main(String[] args) throws IOException { // TODO Auto-generated method stub String expStr = getExpStr(); // a+b HashMap<String, Integer> var = getValue(expStr);// var {a=10, b=20} Calculator calculator = new Calculator(expStr); System.out.println("運算結果:" + expStr + "=" + calculator.run(var)); } // 獲得表示式 public static String getExpStr() throws IOException { System.out.print("請輸入表示式:"); return (new BufferedReader(new InputStreamReader(System.in))).readLine(); } // 獲得值對映 public static HashMap<String, Integer> getValue(String expStr) throws IOException { HashMap<String, Integer> map = new HashMap<>(); for (char ch : expStr.toCharArray()) { if (ch != '+' && ch != '-') { if (!map.containsKey(String.valueOf(ch))) { System.out.print("請輸入" + String.valueOf(ch) + "的值:"); String in = (new BufferedReader(new InputStreamReader(System.in))).readLine(); map.put(String.valueOf(ch), Integer.valueOf(in)); } } } return map; } }

執行結果

注意事項和細節:

1) 當有一個語言需要解釋執行,可將該語言中的句子表示為一個抽象語法樹,就可以 考慮使用直譯器模式,讓程式具有良好的擴充套件性

2) 應用場景:編譯器、運算表示式計算、正則表示式、機器人等

3) 使用直譯器可能帶來的問題:直譯器模式會引起類膨脹、直譯器模式採用遞迴呼叫 方法,將會導致除錯非常複雜、效率可能降低.