面向對象的設計模式(十三),解釋器模式
解釋器模式,從字面上解釋來說就是為一個文法(具有特定語法的形式的語句或表達式)構造解釋器,這個解釋器用來解釋這個文法,使得這樣的具有某種書寫規則的文法能夠表示特定的功能,這樣的特定書寫規則也就是通常所說的語法,如C/C++,Java,Python等計算機語言有自己的語法。還有,一些解釋型語言如Python。它在執行的時候須要Python解釋器,這也就是一種解釋器。
定義:解釋器模式為一些具有特定書寫規則的文法編寫解釋器,從而使得它具有某種意義。
使用場景:
作為解釋器。
解釋具有特定語法的語句的功能或者意義。如解釋具有特定書寫規則的語句,在以下的這個樣例中就是作為解釋器來解釋運算表達式。
作為翻譯機或者譯碼器。如高級語言中的常量,編譯的時候編譯器系統會將常量替換成它代表的數一樣,相似的我們也能夠定義一些特定的具有某種意義的語句。然後用對應的解釋器來解釋器它。
假設又這麽個表達式或語句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等形式結構的語句的意思。這裏的符號_是我規定用來分隔數字的,當然你也能夠規定其它符號作為分隔符。
這個類的方法也非常easy,構造方法中先是將表達式按符號_分隔。得到一些運算符和數字,然後在依據分隔出來的字符串的第一個字符的類型推斷是+運算符還是-運算符還是數字,假設是+運算符。那麽就將存儲的上一個數字彈出棧並記錄到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這樣的形式的語句,事實上這個解釋器實現的功能相似譯碼的功能或者翻譯的功能。只是話說回來,盡管是成功解釋了那種形式的語句,可是也僅僅能計算整數的加減法。假設想要做其它運算或者其它類型的數字,如乘除法和浮點型,那麽須要加入對應的解釋器,但假設涉及到混合運算的時候,那復雜多了,還得考慮優先級。這個時候這樣的模式可能就不適合了,也就是說解釋器模式適合於簡單的語句。
總結:
長處:
- 靈活的擴展性。
當我們對語法規則擴展延伸適合,僅僅須要加入對應的非終結符解釋器(如上面的加減法解釋器),並在構建抽象語法樹時,使用到新添加的解釋器對象(如加入減法解釋器)進行詳細的解釋(計算減法)就可以,非常方便。非終結符也就是還沒有結束的符號,如以下樣例中的加法和減法解釋器分別解釋的加好和減號一樣,它們是二目運算符。其兩邊肯定要兩個數。
缺點:
- 類數量膨脹,後期維護困難。由於對於每一條文法都對應至少一個解釋器類,會產生大量的類,導致後期維護困難。同一時候,對於復雜的文法,構建其抽象的語法樹會顯得比較繁瑣,甚至須要構建多顆語法樹,因此。對於復雜的文法並不推薦使用解釋器模式。
面向對象的設計模式(十三),解釋器模式