解釋器模式,從字面上解釋來說就是為一個文法(具有特定語法的形式的語句或表達式)構造解釋器,這個解釋器用來解釋這個文法,使得這種具有某種書寫規則的文法能夠表示特定的功能,這種特定書寫規則也就是通常所說的語法,如C/C++,Java,Python等計算機語言有自己的語法。還有,一些解釋型語言如Python,它在運行的時候需要Python解釋器,這也就是一種解釋器。
定義:解釋器模式為一些具有特定書寫規則的文法編寫解釋器,從而使得它具有某種意義。
使用場景:
1.
作為解釋器。解釋具有特定語法的語句的功能或者意義。如解釋具有特定書寫規則的語句,在下面的這個例子中就是作為解釋器來解釋運算表達式;
1.
作為翻譯機或者譯碼器。如高級語言中的常量,編譯的時候編譯器系統會將常量替換成它代表的數一樣,類似的我們也可以定義一些特定的具有某種意義的語句,然后用相應的解釋器來解釋器它。
假設又這么個表達式或語句a_+_b_-_c,我們想要知道它的意思,假如我們規定_符號用來分隔數字和運算符,那么上面的這個語句表達的意思就是計算a+b-c,這和學生考試的時候,它們規定1表示A,2表示B,3表示C,4表示D來傳選擇題答案一樣的,類似于編碼譯碼過程,其實譯碼器也就是解釋器,那么我們通過代碼來解釋上面表達式的意思。
代碼實現:
抽象解釋器(基類)
~~~
/**
* 基本的解釋器,是所有解釋器的基類
* @author lt
*
*/
public abstract class BaseInterpreter<T> {
/**
* 抽象解釋方法
* @return
*/
public abstract T interpret();
}
~~~
抽取了解釋器的共性
整數解釋器(解釋整數)
~~~
/**
* 整數解釋器
* @author lt
*
*/
public class NumberInterpreter extends BaseInterpreter<Integer>{
private int num;
public NumberInterpreter(int num){
this.num = num;
}
@Override
public Integer interpret() {
return this.num; // 自動裝箱拆箱
}
}
~~~
解釋整數,直接返回這個整數,這里的整數也就是終結符
運算符解釋器(基類)
~~~
/**
* 二目運算符操作解釋器,也是一個基類,因為有好多的二目運算符
* @author lt
*
*/
public abstract class OperatorInterpreter extends BaseInterpreter<Integer>{
protected BaseInterpreter<Integer> exp1;
protected BaseInterpreter<Integer> exp2;
public OperatorInterpreter(BaseInterpreter<Integer> exp1,BaseInterpreter<Integer> exp2){
this.exp1 = exp1;
this.exp2 = exp2;
}
}
~~~
解釋二目運算符,需要兩個整數,為非終結符解釋器。
加法解釋器(計算加法)
~~~
/**
* 加法解釋器,計算加法
* @author lt
*
*/
public class AdditionInterpreter extends OperatorInterpreter{
public AdditionInterpreter(BaseInterpreter<Integer> exp1,
BaseInterpreter<Integer> exp2) {
super(exp1, exp2);
}
/**
* 用來計算加法
*/
@Override
public Integer interpret() {
return exp1.interpret() + exp2.interpret();
}
}
~~~
減法解釋器(計算減法)
~~~
/**
* 減法計算器
* @author lt
*
*/
public class SubtractionInterpreter extends OperatorInterpreter{
public SubtractionInterpreter(BaseInterpreter<Integer> exp1,
BaseInterpreter<Integer> exp2) {
super(exp1, exp2);
}
@Override
public Integer interpret() {
return exp1.interpret() - exp2.interpret();
}
}
~~~
計算器,翻譯a_+_b_-_c(計算表達式)
~~~
import java.util.Stack;
/**
* 計算器
* @author lt
*
*/
public class Calculator {
private Stack<BaseInterpreter<Integer>> mExpStack = new Stack<BaseInterpreter<Integer>>();
public Calculator(String expression){
// 聲明兩個BaseInterpreter<Integer>的臨時變量,因為計算必須要記錄兩個數
BaseInterpreter<Integer> exp1,exp2;
// 以符號_分隔,這是我們自己規定的
String[] exps = expression.split("_");
for(int i=0;i<exps.length;i++){
switch (exps[i].charAt(0)) {
case '+': // 加法
exp1 = mExpStack.pop();
exp2 = new NumberInterpreter(Integer.valueOf(exps[++i]));
mExpStack.push(new AdditionInterpreter(exp1, exp2));
break;
case '-':
exp1 = mExpStack.pop();
exp2 = new NumberInterpreter(Integer.valueOf(exps[++i]));
mExpStack.push(new SubtractionInterpreter(exp1, exp2));
break;
default: // 數字
mExpStack.push(new NumberInterpreter(Integer.valueOf(exps[i])));
break;
}
}
}
/**
* 計算
* @return
*/
public int calculate(){
return mExpStack.pop().interpret();
}
}
~~~
這個類用來翻譯a_+_b_-_c等形式結構的語句的意思,這里的符號_是我規定用來分隔數字的,當然你也可以規定其他符號作為分隔符。這個類的方法也很簡單,構造方法中先是將表達式按符號_分隔,得到一些運算符和數字,然后在根據分隔出來的字符串的第一個字符的類型判斷是+運算符還是-運算符還是數字,如果是+運算符,那么就將存儲的上一個數字彈出棧并記錄到exp1,然后得到運算符后面的那個字符串(肯定是數字)并記錄到變量exp2中,最后用加法解釋器解釋這兩個變量記錄的數字并壓入棧,等下一次循環的時候彈出和下一個數字進行計算;如果是-運算符,那么和+運算符是一樣的,只不過用的是減法解釋器;如果是數字,直接壓入棧,整個過程是逐步計算的過程。*calculate*方法用來輸出計算結果。
測試:
~~~
public class Test {
public static void main(String[] args) {
String testStr = "1_+_34_-_10_+_50";
Calculator calculator = new Calculator(testStr);
System.out.println("result="+calculator.calculate());
}
}
~~~
結果:

可以看到我們定義的解釋器成功解釋了a_+_b_-_c這種形式的語句,其實這個解釋器實現的功能類似譯碼的功能或者翻譯的功能。不過話說回來,雖然是成功解釋了那種形式的語句,但是也只能計算整數的加減法,如果想要做其他運算或者其他類型的數字,如乘除法和浮點型,那么需要添加相應的解釋器,但如果涉及到混合運算的時候,那復雜多了,還得考慮優先級,這個時候這種模式可能就不適合了,也就是說解釋器模式適合于簡單的語句。
總結:
優點:
- **靈活的擴展性**。當我們對語法規則擴展延伸適合,只需要添加相應的非終結符解釋器(如上面的加減法解釋器),并在構建抽象語法樹時,使用到新增加的解釋器對象(如添加減法解釋器)進行具體的解釋(計算減法)即可,非常方便。非終結符也就是還沒有結束的符號,如下面例子中的加法和減法解釋器分別解釋的加好和減號一樣,它們是二目運算符,其兩邊肯定要兩個數。
缺點:
- **類數量膨脹,后期維護困難**。因為對于每一條文法都對應至少一個解釋器類,會產生大量的類,導致后期維護困難;同時,對于復雜的文法,構建其抽象的語法樹會顯得比較繁瑣,甚至需要構建多顆語法樹,因此,對于復雜的文法并不推薦使用解釋器模式。