一、引子
其實沒有什么好的例子引入解釋器模式,因為它描述了如何構成一個簡單的語言解釋器,主要應用在使用面向對象語言開發編譯器中;在實際應用中,我們可能很少碰到去構造一個語言的文法的情況。
雖然你幾乎用不到這個模式,但是看一看還是能受到一定的啟發的。
二、定義與結構
解釋器模式的定義如下:定義語言的文法,并且建立一個解釋器來解釋該語言中的句子。它屬于類的行為模式。這里的語言意思是使用規定格式和語法的代碼。
在GOF的書中指出:如果一種特定類型的問題發生的頻率足夠高,那么可能就值得將該問題的各個實例表述為一個簡單語言中的句子。這樣就可以構建一個解釋器,該解釋器通過解釋這些句子來解決該問題。而且當文法簡單、效率不是關鍵問題的時候效果最好。
這也就是解釋器模式應用的環境了。
讓我們來看看神秘的解釋器模式是由什么來組成的吧。
1) 抽象表達式角色:聲明一個抽象的解釋操作,這個接口為所有具體表達式角色(抽象語法樹中的節點)都要實現的。
什么叫做抽象語法樹呢?《java與模式》中給的解釋為:抽象語法樹的每一個節點都代表一個語句,而在每個節點上都可以執行解釋方法。這個解釋方法的執行就代表這個語句被解釋。由于每一個語句都代表這個語句被解釋。由于每一個語句都代表一個常見的問題的實例,因此每一個節點上的解釋操作都代表對一個問題實例的解答。
2) 終結符表達式角色:具體表達式。
a) 實現與文法中的終結符相關聯的解釋操作
b) 而且句子中的每個終結符需要該類的一個實例與之對應
3) 非終結符表達式角色:具體表達式。
a) 文法中的每條規則R::=R1R2…Rn都需要一個非終結符表帶式角色
b) 對于從R1到Rn的每個符號都維護一個抽象表達式角色的實例變量
c) 實現解釋操作,解釋一般要遞歸地調用表示從R1到Rn的那些對象的解釋操作
4) 上下文(環境)角色:包含解釋器之外的一些全局信息。
5) 客戶角色:
a) 構建(或者被給定)表示該文法定義的語言中的一個特定的句子的抽象語法樹
b) 調用解釋操作
放上張解釋器結構類圖吧,這也是來自于GOF的書中。
對每一個角色都給出了詳細的職責,而且在類圖中給出五個角色之間的關系。這樣實現起來也不是很困難了,下面舉了一個簡單的例子,希望能加深你對解釋器模式的理解。
三、舉例
來舉一個加減乘除的例子吧,實現思路來自于《java與模式》中的例子。每個角色的功能按照上面提到的規范來實現。
解釋器模式并沒有說明如何創建一個抽象語法樹,因此它的實現可以多種多樣,在上面我們是直接在Test中提供的,當然還有更好、更專業的實現方式。
對于終結符,GOF建議采用享元模式來共享它們的拷貝,因為它們要多次重復出現。但是考慮到享元模式的使用局限性,我建議還是當你的系統中終結符重復的足夠多的時候再考慮享元模式。
四、優缺點
解釋器模式提供了一個簡單的方式來執行語法,而且容易修改或者擴展語法。一般系統中很多類使用相似的語法,可以使用一個解釋器來代替為每一個規則實現一個解釋器。而且在解釋器中不同的規則是由不同的類來實現的,這樣使得添加一個新的語法規則變得簡單。
但是解釋器模式對于復雜文法難以維護。可以想象一下,每一個規則要對應一個處理類,而且這些類還要遞歸調用抽象表達式角色,多如亂麻的類交織在一起是多么恐怖的一件事啊!
五、總結
這樣對解釋器模式應該有了些大體的認識了吧,由于這個模式使用的案例匱乏,所以本文大部分觀點直接來自于GOF的原著。只是實例代碼是親自實現并調試通過的。
其實沒有什么好的例子引入解釋器模式,因為它描述了如何構成一個簡單的語言解釋器,主要應用在使用面向對象語言開發編譯器中;在實際應用中,我們可能很少碰到去構造一個語言的文法的情況。
雖然你幾乎用不到這個模式,但是看一看還是能受到一定的啟發的。
二、定義與結構
解釋器模式的定義如下:定義語言的文法,并且建立一個解釋器來解釋該語言中的句子。它屬于類的行為模式。這里的語言意思是使用規定格式和語法的代碼。
在GOF的書中指出:如果一種特定類型的問題發生的頻率足夠高,那么可能就值得將該問題的各個實例表述為一個簡單語言中的句子。這樣就可以構建一個解釋器,該解釋器通過解釋這些句子來解決該問題。而且當文法簡單、效率不是關鍵問題的時候效果最好。
這也就是解釋器模式應用的環境了。
讓我們來看看神秘的解釋器模式是由什么來組成的吧。
1) 抽象表達式角色:聲明一個抽象的解釋操作,這個接口為所有具體表達式角色(抽象語法樹中的節點)都要實現的。
什么叫做抽象語法樹呢?《java與模式》中給的解釋為:抽象語法樹的每一個節點都代表一個語句,而在每個節點上都可以執行解釋方法。這個解釋方法的執行就代表這個語句被解釋。由于每一個語句都代表這個語句被解釋。由于每一個語句都代表一個常見的問題的實例,因此每一個節點上的解釋操作都代表對一個問題實例的解答。
2) 終結符表達式角色:具體表達式。
a) 實現與文法中的終結符相關聯的解釋操作
b) 而且句子中的每個終結符需要該類的一個實例與之對應
3) 非終結符表達式角色:具體表達式。
a) 文法中的每條規則R::=R1R2…Rn都需要一個非終結符表帶式角色
b) 對于從R1到Rn的每個符號都維護一個抽象表達式角色的實例變量
c) 實現解釋操作,解釋一般要遞歸地調用表示從R1到Rn的那些對象的解釋操作
4) 上下文(環境)角色:包含解釋器之外的一些全局信息。
5) 客戶角色:
a) 構建(或者被給定)表示該文法定義的語言中的一個特定的句子的抽象語法樹
b) 調用解釋操作
放上張解釋器結構類圖吧,這也是來自于GOF的書中。
|
對每一個角色都給出了詳細的職責,而且在類圖中給出五個角色之間的關系。這樣實現起來也不是很困難了,下面舉了一個簡單的例子,希望能加深你對解釋器模式的理解。
三、舉例
來舉一個加減乘除的例子吧,實現思路來自于《java與模式》中的例子。每個角色的功能按照上面提到的規范來實現。
//上下文(環境)角色,使用HashMap來存儲變量對應的數值
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 ; } } //抽象表達式角色,也可以用接口來實現 abstract class Expression { public abstract int interpret(Context con); } //終結符表達式角色 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為調用interpret方法的Variable對象 return con.LookupValue(this); } } //非終結符表達式角色 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("被除數為0!"); return -11111; } } } //測試程序,計算 (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(); //設置變量、常量 Variable a = new Variable(); Variable b = new Variable(); Constant c = new Constant(2); //為變量賦值 con.addValue(a , 5); con.addValue(b , 7); //運算,對句子的結構由我們自己來分析,構造 ex = new Division(new Multiply(a , b), new Add(new Subtract(a , b) , c)); System.out.println("運算結果為:"+ex.interpret(con)); } } |
解釋器模式并沒有說明如何創建一個抽象語法樹,因此它的實現可以多種多樣,在上面我們是直接在Test中提供的,當然還有更好、更專業的實現方式。
對于終結符,GOF建議采用享元模式來共享它們的拷貝,因為它們要多次重復出現。但是考慮到享元模式的使用局限性,我建議還是當你的系統中終結符重復的足夠多的時候再考慮享元模式。
四、優缺點
解釋器模式提供了一個簡單的方式來執行語法,而且容易修改或者擴展語法。一般系統中很多類使用相似的語法,可以使用一個解釋器來代替為每一個規則實現一個解釋器。而且在解釋器中不同的規則是由不同的類來實現的,這樣使得添加一個新的語法規則變得簡單。
但是解釋器模式對于復雜文法難以維護。可以想象一下,每一個規則要對應一個處理類,而且這些類還要遞歸調用抽象表達式角色,多如亂麻的類交織在一起是多么恐怖的一件事啊!
五、總結
這樣對解釋器模式應該有了些大體的認識了吧,由于這個模式使用的案例匱乏,所以本文大部分觀點直接來自于GOF的原著。只是實例代碼是親自實現并調試通過的。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
