|
一、引子
其實(shí)沒(méi)有什么好的例子引入解釋器模式,因?yàn)樗枋隽巳绾螛?gòu)成一個(gè)簡(jiǎn)單的語(yǔ)言解釋器,主要應(yīng)用在使用面向?qū)ο笳Z(yǔ)言開(kāi)發(fā)編譯器中;在實(shí)際應(yīng)用中,我們可能很少碰到去構(gòu)造一個(gè)語(yǔ)言的文法的情況。
雖然你幾乎用不到這個(gè)模式,但是看一看還是能受到一定的啟發(fā)的。
二、定義與結(jié)構(gòu)
解釋器模式的定義如下:定義語(yǔ)言的文法,并且建立一個(gè)解釋器來(lái)解釋該語(yǔ)言中的句子。它屬于類(lèi)的行為模式。這里的語(yǔ)言意思是使用規(guī)定格式和語(yǔ)法的代碼。
在GOF的書(shū)中指出:如果一種特定類(lèi)型的問(wèn)題發(fā)生的頻率足夠高,那么可能就值得將該問(wèn)題的各個(gè)實(shí)例表述為一個(gè)簡(jiǎn)單語(yǔ)言中的句子。這樣就可以構(gòu)建一個(gè)解釋器,該解釋器通過(guò)解釋這些句子來(lái)解決該問(wèn)題。而且當(dāng)文法簡(jiǎn)單、效率不是關(guān)鍵問(wèn)題的時(shí)候效果最好。
這也就是解釋器模式應(yīng)用的環(huán)境了。
讓我們來(lái)看看神秘的解釋器模式是由什么來(lái)組成的吧。
1) 抽象表達(dá)式角色:聲明一個(gè)抽象的解釋操作,這個(gè)接口為所有具體表達(dá)式角色(抽象語(yǔ)法樹(shù)中的節(jié)點(diǎn))都要實(shí)現(xiàn)的。
什么叫做抽象語(yǔ)法樹(shù)呢?《java與模式》中給的解釋為:抽象語(yǔ)法樹(shù)的每一個(gè)節(jié)點(diǎn)都代表一個(gè)語(yǔ)句,而在每個(gè)節(jié)點(diǎn)上都可以執(zhí)行解釋方法。這個(gè)解釋方法的執(zhí)行就代表這個(gè)語(yǔ)句被解釋。由于每一個(gè)語(yǔ)句都代表這個(gè)語(yǔ)句被解釋。由于每一個(gè)語(yǔ)句都代表一個(gè)常見(jiàn)的問(wèn)題的實(shí)例,因此每一個(gè)節(jié)點(diǎn)上的解釋操作都代表對(duì)一個(gè)問(wèn)題實(shí)例的解答。
2) 終結(jié)符表達(dá)式角色:具體表達(dá)式。
a) 實(shí)現(xiàn)與文法中的終結(jié)符相關(guān)聯(lián)的解釋操作
b) 而且句子中的每個(gè)終結(jié)符需要該類(lèi)的一個(gè)實(shí)例與之對(duì)應(yīng)
3) 非終結(jié)符表達(dá)式角色:具體表達(dá)式。
a) 文法中的每條規(guī)則R::=R1R2…Rn都需要一個(gè)非終結(jié)符表帶式角色
b) 對(duì)于從R1到Rn的每個(gè)符號(hào)都維護(hù)一個(gè)抽象表達(dá)式角色的實(shí)例變量
c) 實(shí)現(xiàn)解釋操作,解釋一般要遞歸地調(diào)用表示從R1到Rn的那些對(duì)象的解釋操作
4) 上下文(環(huán)境)角色:包含解釋器之外的一些全局信息。
5) 客戶(hù)角色:
a) 構(gòu)建(或者被給定)表示該文法定義的語(yǔ)言中的一個(gè)特定的句子的抽象語(yǔ)法樹(shù)
b) 調(diào)用解釋操作
放上張解釋器結(jié)構(gòu)類(lèi)圖吧,這也是來(lái)自于GOF的書(shū)中。

對(duì)每一個(gè)角色都給出了詳細(xì)的職責(zé),而且在類(lèi)圖中給出五個(gè)角色之間的關(guān)系。這樣實(shí)現(xiàn)起來(lái)也不是很困難了,下面舉了一個(gè)簡(jiǎn)單的例子,希望能加深你對(duì)解釋器模式的理解。
三、舉例
來(lái)舉一個(gè)加減乘除的例子吧,實(shí)現(xiàn)思路來(lái)自于《java與模式》中的例子。每個(gè)角色的功能按照上面提到的規(guī)范來(lái)實(shí)現(xiàn)。
//上下文(環(huán)境)角色,使用HashMap來(lái)存儲(chǔ)變量對(duì)應(yīng)的數(shù)值
class Context { private Map valueMap = new HashMap(); public void addValue(Variable x , int y) { Integer yi = new Integer(y); valueMap.put(x , yi); }
public int LookupValue(Variable x) { int i = ((Integer)valueMap.get(x)).intValue(); return i ; } }
//抽象表達(dá)式角色,也可以用接口來(lái)實(shí)現(xiàn)
abstract class Expression { public abstract int interpret(Context con); }
//終結(jié)符表達(dá)式角色
class Constant extends Expression { private int i ; public Constant(int i) { this.i = i; }
public int interpret(Context con) { return i ; } }
class Variable extends Expression { public int interpret(Context con) { //this為調(diào)用interpret方法的Variable對(duì)象 return con.LookupValue(this); } }
//非終結(jié)符表達(dá)式角色
class Add extends Expression { private Expression left ,right ; public Add(Expression left , Expression right) { this.left = left ; this.right= right ; }
public int interpret(Context con) { return left.interpret(con) + right.interpret(con); } }
class Subtract extends Expression { private Expression left , right ; public Subtract(Expression left , Expression right) { this.left = left ; this.right= right ; }
public int interpret(Context con) { return left.interpret(con) - right.interpret(con); }
}
class Multiply extends Expression { private Expression left , right ; public Multiply(Expression left , Expression right) { this.left = left ; this.right= right ; } public int interpret(Context con) { return left.interpret(con) * right.interpret(con); } }
class Division extends Expression { private Expression left , right ; public Division(Expression left , Expression right) { this.left = left ; this.right= right ; }
public int interpret(Context con) { try{ return left.interpret(con) / right.interpret(con); }catch(ArithmeticException ae) { System.out.println("被除數(shù)為0!"); return -11111; } } }
//測(cè)試程序,計(jì)算 (a*b)/(a-b+2)
public class Test { private static Expression ex ; private static Context con ; public static void main(String[] args) { con = new Context(); //設(shè)置變量、常量 Variable a = new Variable(); Variable b = new Variable(); Constant c = new Constant(2); //為變量賦值 con.addValue(a , 5); con.addValue(b , 7); //運(yùn)算,對(duì)句子的結(jié)構(gòu)由我們自己來(lái)分析,構(gòu)造 ex = new Division(new Multiply(a , b), new Add(new Subtract(a , b) , c)); System.out.println("運(yùn)算結(jié)果為:"+ex.interpret(con)); } }
解釋器模式并沒(méi)有說(shuō)明如何創(chuàng)建一個(gè)抽象語(yǔ)法樹(shù),因此它的實(shí)現(xiàn)可以多種多樣,在上面我們是直接在Test中提供的,當(dāng)然還有更好、更專(zhuān)業(yè)的實(shí)現(xiàn)方式。
對(duì)于終結(jié)符,GOF建議采用享元模式來(lái)共享它們的拷貝,因?yàn)樗鼈円啻沃貜?fù)出現(xiàn)。但是考慮到享元模式的使用局限性,我建議還是當(dāng)你的系統(tǒng)中終結(jié)符重復(fù)的足夠多的時(shí)候再考慮享元模式。
四、優(yōu)缺點(diǎn)
解釋器模式提供了一個(gè)簡(jiǎn)單的方式來(lái)執(zhí)行語(yǔ)法,而且容易修改或者擴(kuò)展語(yǔ)法。一般系統(tǒng)中很多類(lèi)使用相似的語(yǔ)法,可以使用一個(gè)解釋器來(lái)代替為每一個(gè)規(guī)則實(shí)現(xiàn)一個(gè)解釋器。而且在解釋器中不同的規(guī)則是由不同的類(lèi)來(lái)實(shí)現(xiàn)的,這樣使得添加一個(gè)新的語(yǔ)法規(guī)則變得簡(jiǎn)單。
但是解釋器模式對(duì)于復(fù)雜文法難以維護(hù)。可以想象一下,每一個(gè)規(guī)則要對(duì)應(yīng)一個(gè)處理類(lèi),而且這些類(lèi)還要遞歸調(diào)用抽象表達(dá)式角色,多如亂麻的類(lèi)交織在一起是多么恐怖的一件事啊!
五、總結(jié)
這樣對(duì)解釋器模式應(yīng)該有了些大體的認(rèn)識(shí)了吧,由于這個(gè)模式使用的案例匱乏,所以本文大部分觀點(diǎn)直接來(lái)自于GOF的原著。只是實(shí)例代碼是親自實(shí)現(xiàn)并調(diào)試通過(guò)的。
|