設計模式--直譯器模式Interpreter(行為型)

benbenxiongyuan發表於2014-04-16

1 定義:

1.1 定義:Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language.(給定一門語言,定義它的方法的一種表示,並定義一個直譯器,該直譯器使用表示來解釋語言中的句子。)

1.2 通用類圖:

角色說明:

AbstractExpression——抽象直譯器

具體的解釋任務由各個子類完成,然而首先通過它,來向下遞迴解析。

TerminalExpression——終結符表示式

實現與文法中的元素相關聯的解釋操作,通常一個直譯器模式中只有一個終結符表示式(類),但有多個例項,對應不同的終結符。

NonterminalExpression——非終結符表示式

文法中的每條規則對應於一個非終結符表示式(類)(例如:加減法規則分別對應到AddExpression, SubExpression兩個類)。非終結符表示式根據邏輯的複雜程度而增加,原則上每個文法規則都對應一個非終結符表示式。

Context——環境角色

直譯器之外的一些全域性資訊(多為:終結符與值的對映表)。

Client——客戶角色

構建表示該方法定義的語言中的一個特定的句子的抽象語法樹,並呼叫解釋操作

執行流程:將一個待解析的輸入語句構建成一個抽象語法樹(該語法樹此時由直譯器中定義的表示式構成),然後將該語法樹(表示式)交給表示式物件去解釋(當然是遞迴的)。

1.3 通用程式碼:

 
public abstract class Expression {
	// 每個表示式必須有一個解析任務
	public abstract Object interpreter(Context ctx);
}

public class TerminalExpression extends Expression {
	// 通常終結符表示式只有一個,但是有多個物件
	public Object interpreter(Context ctx) {
		return null;
	}
}

public class NonterminalExpression extends Expression {
	// 每個非終結符表示式都會對其他表示式產生依賴
	public NonterminalExpression(Expression... expression) {
	}

	public Object interpreter(Context ctx) {
		// 進行文法處理
		return null;
	}
}

public class Context {
}

public class Client {
	public static void main(String[] args) {
		Context ctx = new Context();
		// 通常定一個語法容器,容納一個具體的表示式,通常為ListArray,LinkedList,Stack等型別
		Stack<Expression> stack = null;
		/*
		 * for(;;){ //進行語法判斷,併產生遞迴呼叫 }
		 */
		// 產生一個完整的語法樹,由各各個具體的語法分析進行解析
		Expression exp = stack.pop();
		// 具體元素進入場景
		exp.interpreter(ctx);
	}
}


2 優點

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

3 缺點

3.1 類膨脹:每個語法都要產生一個非終結符表示式,語法規則比較複雜時,就可能產生大量的類檔案,為維護帶來了非常多的麻煩;

3.2 不便於除錯:因為採用遞迴呼叫,若要排查錯誤,則非常複雜;

3.3 效率問題:由於使用了大量的迴圈和遞迴,效率是一個不容忽視的問題。

4 應用場景

4.1 重複發生的問題可以使用直譯器模式:如伺服器日誌檔案的分析處理;

4.2 簡單語法需要解釋:不過已逐步被被專用工具所取代;

5 注意事項

儘量不要在重要的模組中使用直譯器模式,否則維護是一個很大的問題。在專案中可以使用shellJRubyGroovy等指令碼語言來代替直譯器模式,彌補Java編譯型語言的不足。(而且已有眾多開源直譯器,沒必要自己寫。)

6 擴充套件

暫無

7 範例

1


很簡單的一個類圖,VarExpression用來解析運算元素,各個公式能運算元素的數量是不同的,但每個運算元素都對應一個VarExpression物件。SybmolExpression負責運算子號解析,由兩個子類AddExpression(負責加法運算)和SubExpression(負責減法運算)來實現。解析的工作完成了,我們還需要把安排執行的先後順序(加減法是不用考慮,但是乘除法呢?注意擴充套件性),並且還要返回結果,因此我們需要增加一個封裝類來進行封裝處理,由於我們只做運算,暫時還不與業務有關聯,定義為Calculator類,分析到這裡,思路就比較清晰了,優化後加減法類圖如圖27-2所示。

圖2


優化後加減法類圖


圖27-3 完整加減法類圖

原始碼如下:

  1. /** 
  2.  * @author cbf4Life cbf4life@126.com I'm glad to share my knowledge with you 
  3.  *         all. 抽象表示式 
  4.  */  
  5. public abstract class Expression {  
  6.     // 解析公式和數值,其中var中的key值是是公式中的引數,value值是具體的數字   
  7.     public abstract int interpreter(HashMap<String, Integer> var);  
  8. }  
  9.   
  10. public class VarExpression extends Expression {  
  11.     private String key;  
  12.   
  13.     public VarExpression(String _key) {  
  14.         this.key = _key;  
  15.     }  
  16.   
  17.     // 從map中取之   
  18.     public int interpreter(HashMap<String, Integer> var) {  
  19.         return var.get(this.key);  
  20.     }  
  21. }  
  22.   
  23. public abstract class SymbolExpression extends Expression {  
  24.     protected Expression left;  
  25.     protected Expression right;  
  26.   
  27.     // 所有的解析公式都應只關心自己左右兩個表示式的結果   
  28.     public SymbolExpression(Expression _left, Expression _right) {  
  29.         this.left = _left;  
  30.         this.right = _right;  
  31.     }  
  32. }  
  33.   
  34. public class AddExpression extends SymbolExpression {  
  35.     public AddExpression(Expression _left, Expression _right) {  
  36.         super(_left, _right);  
  37.     }  
  38.   
  39.     // 把左右兩個表示式運算的結果加起來   
  40.     public int interpreter(HashMap<String, Integer> var) {  
  41.         return super.left.interpreter(var) + super.right.interpreter(var);  
  42.     }  
  43. }  
  44.   
  45. public class SubExpression extends SymbolExpression {  
  46.     public SubExpression(Expression _left, Expression _right) {  
  47.         super(_left, _right);  
  48.     }  
  49.   
  50.     // 解析就是減法運算   
  51.     public int interpreter(HashMap<String, Integer> var) {  
  52.         return super.left.interpreter(var) - super.right.interpreter(var);  
  53.     }  
  54. }  
  55.   
  56. public class Calculator {  
  57.     // 定義的表示式   
  58.     private Expression expression;  
  59.   
  60.     // 建構函式傳參,並解析   
  61.     public Calculator(String expStr) {  
  62.         // 定義一個堆疊,安排運算的先後順序   
  63.         Stack<Expression> stack = new Stack<Expression>();  
  64.         // 表示式拆分為字元陣列   
  65.         char[] charArray = expStr.toCharArray();  
  66.         // 運算   
  67.         Expression left = null;  
  68.         Expression right = null;  
  69.         for (int i = 0; i < charArray.length; i++) {  
  70.             switch (charArray[i]) {  
  71.             case '+'// 加法   
  72.                 // 加法結果放到堆疊中   
  73.                 left = stack.pop();  
  74.                 right = new VarExpression(String.valueOf(charArray[++i]));  
  75.                 stack.push(new AddExpression(left, right));  
  76.                 break;  
  77.             case '-':  
  78.                 left = stack.pop();  
  79.                 right = new VarExpression(String.valueOf(charArray[++i]));  
  80.                 stack.push(new SubExpression(left, right));  
  81.                 break;  
  82.             default// 公式中的變數   
  83.                 stack.push(new VarExpression(String.valueOf(charArray[i])));  
  84.             }  
  85.         }  
  86.         // 把運算結果丟擲來   
  87.         this.expression = stack.pop();  
  88.     }  
  89.   
  90.     // 開始運算   
  91.     public int run(HashMap<String, Integer> var) {  
  92.         return this.expression.interpreter(var);  
  93.     }  
  94. }  
  95.   
  96. public class Client {  
  97.     // 執行四則運算   
  98.     public static void main(String[] args) throws IOException {  
  99.         String expStr = getExpStr();  
  100.         // 賦值   
  101.         HashMap<String, Integer> var = getValue(expStr);  
  102.         Calculator cal = new Calculator(expStr);  
  103.         System.out.println("運算結果為:" + expStr + "=" + cal.run(var));  
  104.     }  
  105.   
  106.     // 獲得表示式   
  107.     public static String getExpStr() throws IOException {  
  108.         System.out.print("請輸入表示式:");  
  109.         return (new BufferedReader(new InputStreamReader(System.in)))  
  110.                 .readLine();  
  111.     }  
  112.   
  113.     // 獲得值對映   
  114.     public static HashMap<String, Integer> getValue(String exprStr)  
  115.             throws IOException {  
  116.         HashMap<String, Integer> map = new HashMap<String, Integer>();  
  117.         // 解析有幾個引數要傳遞   
  118.         for (char ch : exprStr.toCharArray()) {  
  119.             if (ch != '+' && ch != '-') {  
  120.                 if (!map.containsKey(String.valueOf(ch))) { // 解決重複引數的問題   
  121.                     System.out.print("請輸入" + ch + "的值:");  
  122.                     String in = (new BufferedReader(new InputStreamReader(  
  123.                             System.in))).readLine();  
  124.                     map.put(String.valueOf(ch), Integer.valueOf(in));  
  125.                 }  
  126.             }  
  127.         }  
  128.         return map;  
  129.     }  
  130. }  
 

轉自:http://blog.csdn.net/yuanlong_zheng/article/details/7584871

相關文章