學習設計模式不光要學習設計模式的思想,還要去深入理解,爲什麼要用這個設計模式。
如何深入理解?讀優秀的框架程式碼,看別人程式碼,瞭解它們的使用場景。 - - - 博主老師(感謝他)
本文介紹瞭直譯器模式的概念,實現
定義:給定一個語言,定義它的文法的一種表示,並定義一個直譯器,該直譯器使用該表示來解釋語言中的句子。
概念有點難懂…下面 下麪會做具體介紹。
(1)什麼是文法
舉個例子:假設有如下短語
我是程式設計師
我是設計師
我是搬運工
上面的短語:「我」可以看作是主語,「是」則表示謂語,「程式設計師」、「設計師」、「搬運工」這些名詞可以看作是賓語,也就是說,上面的短語是「主謂賓」結構,這樣的結構我們稱爲一條文法。可以通過文法來造成更多符合該文法的句子。
文法不止是「主謂賓」、「定狀補」這種語法結構。我們也可以將上面的短語堪稱是一條「我是[名詞]」這樣的結構。這也是一條文法。
再舉個例子:假設有ab開頭ef結尾中間排列N(N>=0)個cd的字串
abcd…cdef
隨着N的不同,具體的字串也不同,我們可以得到「abef」、「abcdef」、「abcdcdef」之類的字串。
在電腦科學中,我們將上述「a」、「b」、「c」、「d」、「e」、「f」這6個字元稱爲一種形式語言的字元表,而字元組成的集合稱爲形式語言。定義符號S,從符號S出發推導上述字串,那麼就可以得到如下推導式:
S ::= abA*ef
A ::= cd
其中「::=」表示推導;符號「*」表示閉包,就是符號A可以有0個或N個重複。其中A、S稱爲非終結符號(能推導出右邊的式子);abcdef爲非終結符號(不能繼續推導)。這個推導式就是文法,並且我們將這種文法稱爲形式文法。形式文法與形式語言相對應,用來描述形式語言。
(2)什麼是直譯器
可以將直譯器理解成一個翻譯機,用來翻譯類似於「abef」、「abcdef」、「abcdcdef」之類的字串句子
再回過頭來看直譯器模式的定義:給定一個語言(如由abcdef六個字元組成的字串集合),定義它的文法的一種表示(如上面的S ::= abAef 和 A ::= cd),並定義一個直譯器,該直譯器使用該表示來解釋語言中的句子。
這樣似乎能理解了。在程式設計中,很少會用標準的文法推導式,很多時候直接使用類似於「AB」這樣的字串規則表達式表示文法。
直譯器模式一個比較常見的場景是對算數表達式的解釋
例如表達式"m + n + p", 如果使用直譯器模式對該表達式進行解釋,那麼代表數位的m、n和p三個字母我們就可以看成是終結符號,"+"這個算術運算子看成是非終結符號。
定義抽象的算數運算直譯器
public abstract class ArithmeticExpression {
public abstract int interpret();
}
數位直譯器
public class NumExpression extends ArithmeticExpression {
private int num;
public NumExpression(int num) {
this.num = num;
}
@Override
public int interpret() {
return num;
}
}
抽象的操作直譯器(因爲可能會有加、減、乘、除等子類)
public abstract class OperatorExpression extends ArithmeticExpression {
// 聲名兩個成員變數儲存運算子號兩邊的數位直譯器
protected ArithmeticExpression exp1, exp2;
public OperatorExpression(ArithmeticExpression exp1, ArithmeticExpression exp2) {
this.exp1 = exp1;
this.exp2 = exp2;
}
}
加號直譯器
public class AdditionExpressioin extends OperatorExpression {
public AdditionExpressioin(ArithmeticExpression exp1, ArithmeticExpression exp2) {
super(exp1, exp2);
}
@Override
public int interpret() {
return exp1.interpret() + exp2.interpret();
}
}
計算業務類
public class Calculator {
private Stack<ArithmeticExpression> mExpStack = new Stack<>();
public Calculator(String expression) {
ArithmeticExpression exp1, exp2;
String[] elements = expression.split(" ");
for (int i = 0; i < elements.length; i++) {
switch (elements[i].charAt(0)) {
case '+':
exp1 = mExpStack.pop();
exp2 = new NumExpression(Integer.valueOf(elements[++i]));
mExpStack.push(new AdditionExpressioin(exp1, exp2));
break;
default:
mExpStack.push(new NumExpression(Integer.valueOf(elements[i])));
break;
}
}
}
public int calculate() {
return mExpStack.pop().interpret();
}
}
測試
public class Test {
public static void main(String[] args) {
Calculator c = new Calculator("123 + 1 + 58 + 888");
System.out.println(c.calculate());
}
}
輸出
1070
上面我們就定義了個加法的運算。如果現在要支援減法,只需要新增一個減法直譯器
public class SubtractionExpression extends OperatorExpression {
public SubtractionExpression(ArithmeticExpression exp1, ArithmeticExpression exp2) {
super(exp1, exp2);
}
@Override
public int interpret() {
return exp1.interpret() - exp2.interpret();
}
}
在業務類裏面加上case '-'的分支
case '-':
exp1 = mExpStack.pop();
exp2 = new NumExpression(Integer.valueOf(elements[++i]));
mExpStack.push(new SubtractionExpression(exp1, exp2));
break;
直譯器模式的靈活性非常強。但是如果要做混合運算(有乘除),就會涉及到優先順序,實現就會複雜的多。
感覺自己的開發中不怎麼會有能用到直譯器模式的場景。在jdk中的正則、Spring的EL表達式等這些都用到瞭直譯器模式,
還有mybatis對sql的解析等等。
適用場景:
何紅輝 關愛民 · Android原始碼設計模式解析於實戰 · 人民郵電出版社
https://www.az1314.cn/art/46