簡介
今天給大家介紹的設計模式叫做“直譯器模式”,該模式是“行為型設計模式”中的一員。
直譯器模式的核心思想是:給定一個語言,定義它的文法的一種表示,並定義一個直譯器,使用該直譯器來解釋語言中的句子。
聽完這句話話是不是頓時感覺一臉懵?什麼語言、文法、句子,都是些什麼鬼?
別慌讓“菜鳥”來給你分析一波。
文法:可以將其理解為一種規則,就好比漢語中一句話必須由“主謂賓”三者構成一樣。句子:符合文法規則的一種表現,可以簡單理解成符合漢語規則的一句話。語言:將所有句子彙集起來的一個集合就是語言。
我們可以透過直譯器將語言中的句子解析成一顆語法樹,從而對其進行操作。所謂的語法樹就是將句子透過文法進行推導,進而獲取到句子的一顆樹形表現形式。
直譯器模式UML類圖類圖講解
Context:環境角色(上下文),含有每個直譯器所需的一些資料或全域性的一些資訊。AbstractExpression:抽象表示式類,聲明瞭抽象的解釋操作,所有直譯器類都繼承或實現該類。TerminalExpression:終結符表示式類,是AbstractExpression的子類,實現了文法中有關終結符相關的解釋操作。TerminalExpression:非終結符表示式,AbstractExpression的子類,該類的功能與終結表示式類相反,文法中所有非終結符由該類進行解釋。Client:客戶端測試類。
案例講解下面讓我們透過一個經典的“計算器”案例來詳細瞭解一下直譯器模式。
注:本文重點講解的是直譯器模式故示例程式碼只實現了加減法的運算。
表示式介面
public interface Expression { /** * 解釋表示式的抽象方法 * * @param map 比如現有表示式:a + b;那麼map中存放的就是{a=10,b=20} * @return 返回解釋後的值。 */ int interpreter(Map<String, Integer> map);}
變數解析器類
public class VarExpression implements Expression { // 公式中的變數。 private String key; public VarExpression(String key) { this.key = key; } // 透過key獲取所對應的值 @Override public int interpreter(Map<String, Integer> map) { return map.get(key); }}
運算子解析器類
public class SymbolExpression implements Expression { /** * 假如現有表示式:a + b - c 需要解析。 * 分析: * 1、一個運算子連線的是它左右兩個數字。 * 2、如上表達式【+】號連線的是吧“a"和"b",【-】號連線的是"a + b"和“c”。 * 3、經次分析我們將運算子連線的左右都看成是一個表示式也就是Expression。 */ // 左表示式 protected Expression leftExpression; // 右表示式 protected Expression rightExpression; public SymbolExpression(Expression leftExpression, Expression rightExpression) { this.leftExpression = leftExpression; this.rightExpression = rightExpression; } // 不同種類的運算子由不同的運算子子類進行解析,所以該類不實現interpreter方法。 @Override public int interpreter(Map<String, Integer> map) { return 0; }}
減法解析器
public class SubExpression extends SymbolExpression { public SubExpression(Expression leftExpression, Expression rightExpression) { super(leftExpression, rightExpression); } // 解釋減法 @Override public int interpreter(Map<String, Integer> map) { return leftExpression.interpreter(map) - rightExpression.interpreter(map); }}
加法解析器
public class AddExpression extends SymbolExpression { public AddExpression(Expression leftExpression, Expression rightExpression) { super(leftExpression, rightExpression); } // 解釋加法 @Override public int interpreter(Map<String, Integer> map) { return leftExpression.interpreter(map) + rightExpression.interpreter(map); }}
計算器 => 對應Context角色
public class Calculator { // 表示式 private Expression expression; public Calculator(String strExpression) { char[] charArray = strExpression.toCharArray(); // 定義棧用於儲存表示式,因示例簡單故不考慮運算順序。 Stack<Expression> stack = new Stack<>(); 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)); break; case '-': // 過程跟加法一樣 left = stack.pop(); right = new VarExpression(String.valueOf(charArray[++i])); stack.push(new SubExpression(left, right)); break; default: // 不是運算子 stack.push(new VarExpression(String.valueOf(charArray[i]))); break; } } // 遍歷完成獲取最終解析好的表示式。 this.expression = stack.pop(); } /** * * @param map 表示式對應的值 * @return 計算的結果 */ public int calculate(Map<String, Integer> map) { return this.expression.interpreter(map); }}
客戶端測試類
public class Client { public static void main(String[] args) { // 表示式 String strExpression = "a+b-c+d"; // 表示式對應的值 Map<String, Integer> map = new HashMap<>(); map.put("a", 2); map.put("b", 10); map.put("c", 8); map.put("d", 13); // 建立計算器 Calculator calculator = new Calculator(strExpression); // 計算 int result = calculator.calculate(map); System.out.println("表示式:" + strExpression + "的計算結果為:" + result); }}
執行結果
總結1、使用直譯器模式可以提高程式碼的可擴充套件性。
2、使用直譯器模式會引起類膨脹。
3、直譯器模式會採用遞迴呼叫方法,可能會降低效率,並且會提高維護和除錯的成本。
4、直譯器模式的使用場景比較少,開發中也不經常接觸到,一般常用在編譯器、運算表示式計算、正則表示式解析等。