定義
給定一個語言,定義它的文法的一種表示,并定義一個解釋器,這個解釋器使用該表示來解釋語言中的句子。
适用性
最典型的應用就是正規表達式
重複發生的問題可以使用解釋器模式:比如根據使用者輸入的公式進行加減乘除四則運算,但是他們輸入的公式每次都不同,有時是a+b-c*d,有時是a*b+c-d,等等等等個,公式千變萬化,但是都是由加減乘除四個非終結符來連接配接的,這時我們就可以使用解釋器模式。
當有一個語言需要解釋執行,并且你可以把該語言中的句子表示為一個抽象的文法樹時,可使用解釋器模式.而當存在以下情況時,該模式的效果最好:
1、該文法簡單,對于複雜的文法,文法的類層次變得龐大而無法管理.此時,文法分析程式生成器這樣得工具時更好得選擇。它們無需建構抽象文法樹即可解釋表達式,這樣可以節省空間而且還可以節省時間;
2、效率不是一個關鍵的問題,最高效的解釋器通常不是通過直接解釋文法分析樹實作的,而是首先把他們轉換成另外一種形式.例如:正規表達式通常被轉換成狀态機。但即使在這種情況下,轉換器仍可用解釋器模式實作,該模式仍是有用的.
模式中的角色
1.AbstractExpression(抽象表達式) 聲明一個抽象的解釋操作,這個接口為抽象文法樹中所有的節點所共享。
2.TerminalExpression(終結符表達式) 實作與文法中的終結符相關聯的解釋操作。 一個句子中的每個終結符需要該類的一個執行個體。
3.NonterminalExpression(非終結符表達式) 為文法中的非終結符實作解釋(Interpret)操作。
4.Context(上下文) 包含解釋器之外的一些全局資訊。
5.Client(客戶) 建構(或被給定)表示該文法定義的語言中一個特定的句子的抽象文法樹。 該抽象文法樹由NonterminalExpression和TerminalExpression的執行個體裝配而成。 調用解釋操作。
角色關系UML
以加減運算為例子
UML圖如下:
java代碼:
抽象表達式:
package demo;
import java.util.Map;
/**
*
* @ClassName: Expression
* @Description:抽象表達式
* @author cheng
* @date
public interface Expression
/**
* 解析公式和數值,其中var中的key是公式的參數,value值是具體的數字
* 負責對傳遞進來的參數和值進行解析和比對,其中key是表達式a+b+c中的a、b、c,value是運算時取得的值
*
* 如果是終結符表達式,那麼此方法将擷取參數的值
* 如果是非終結符表達式,那麼此方法将進行運算,比如加減
**/
int
終結符表達式
package demo;
import java.util.Map;
/**
*
* @ClassName: VarExpression
* @Description:變量解析器/終結符表達式
* @author cheng
* @date
public class VarExpression implements Expression
// 需要擷取值的變量名
private String key;
/**
* 構造函數
*
* @param
public VarExpression(String key) {
this.key = key;
}
/**
* 從map中取值
*/
@Override
public int interpreter(Map<String, Integer> var) {
return
非終結符表達式
package demo;
import java.util.Map;
/**
*
* @ClassName: AddExpression
* @Description: 加法解析器/interpreter方法處理加法運算
* @author cheng
* @date
public class AddExpression implements Expression
// 每個運算符都有左右兩個參數進行運算
private Expression left;
private Expression right;
/**
* 構造函數
*
* @param left
* @param
public AddExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
/**
* 進行加法運算
*/
@Override
public int interpreter(Map<String, Integer> var) {
return
package demo;
import java.util.Map;
/**
*
* @ClassName: SubExpression
* @Description:減法解析器/interpreter方法處理減法運算
* @author cheng
* @date
public class SubExpression implements Expression
// 每個運算符都有左右兩個參數進行運算
private Expression left;
private Expression right;
/**
* 構造函數
*
* @param left
* @param
public SubExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
/**
* 進行減法運算
*/
@Override
public int interpreter(Map<String, Integer> var) {
return
上下文
package demo;
import java.util.Map;
import java.util.Stack;
/**
*
* @ClassName: Context
* @Description:對輸入的表達式進行解析,并計算
* @author cheng
* @date
public class Context
/**
* 定義表達式,最後拿到是一個運算解析器,比如X+Y格式的,其中X可能又是由A+B的運算解析器組成
* 隻有最底層的解析器才是變量解析器,也就是終結符表達式 此參數最終得到的肯定是非終結表達式
* */
private Expression expression;
/**
* 分析使用者輸入的表達式
*/
public void analyse(String expStr) {
// 定義一個棧,安排運算的先後順序
Stack<Expression> stack = new Stack<Expression>();
char[] charArray = expStr.toCharArray();
Expression left = null;
Expression right = null;
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])));
}
}
// 把最終棧的頂層抛出,它即是最後封裝的非終結表達式
this.expression = stack.pop();
}
/**
*
* @Title: run
* @Description: 将鍵值對輸入給表達式運算
* @param var
* @return
public int run(Map<String, Integer> var) {
return
測試
package demo;
import java.util.HashMap;
import java.util.Map;
/**
*
* @ClassName: ClientTest
* @Description:測試
* @author cheng
* @date
public class ClientTest
public static void main(String[] args) {
String expStr = "a+b-c+d+e";
Map<String, Integer> var = new HashMap<String, Integer>();
var.put("a", 1);
var.put("b", 2);
var.put("c", 2);
var.put("d", 4);
var.put("e", 3);
Context context = new Context();
// 先解析運算表達式
context.analyse(expStr);
// 進行運算
運作結果
8
優點
解釋器是一個簡單的文法分析工具,它最顯著的優點就是擴充性,修改文法規則隻需要修改相應的非終結符就可以了,若擴充文法,隻需要增加非終結符類就可以了。
缺點
解釋器模式會引起類的膨脹:每個文法都需要産生一個非終結符表達式,文法規則比較複雜時,就可能産生大量的類檔案,為維護帶來非常多的麻煩。
解釋器模式采用遞歸調用方法:每個非終結符表達式隻關心與自己相關的表達式,每個表達式需要知道最終的結果,必須通過一層一層的剝繭,無論是面向對象的語言還是面向過程的語言,遞歸都是一個不推薦的方式(隻在必要條件下使用),它将導緻調試非常複雜。想想看,如果要排查一個錯誤,我們是不是要一個個斷點調試下去,直至最小的文法單元。
解釋器模式使用了大量的循環和遞歸:效率是一個不容忽視的問題。特别是用于解釋一個解析複雜、冗長的文法時,效率是難以忍受的。