直譯器這個名詞想必大家都不會陌生,比如編譯原理中,乙個算術表示式通過詞法分析器形成詞法單元,而後這些詞法單元再通過語法分析器構建語法分析樹,最終形成一顆抽象的語法分析樹。諸如此類的例子也有很多,比如編譯器、正規表示式等等。
如果一種特定型別的問題發生的頻率足夠高,那麼可能就值得將該問題的各個例項表述為乙個簡單語言中的句子,這樣就可以構建乙個直譯器,該直譯器通過解釋這些句子來解決該問題。
就比如正規表示式,它就是直譯器模型的一種應用,直譯器為正規表示式定義了乙個文法,如何表示乙個特定的正規表示式,以及如何解釋這個正規表示式。
直譯器模式(interpreter),給定乙個語言,定義它的文法的一種表示,並定義乙個直譯器,這個直譯器使用該表示來解釋語言中的句子。uml結構圖如下:
其中,context是環境角色,包含直譯器之外的一些全域性資訊;abstractexpression為抽象表示式,宣告乙個抽象的解釋操作,這個介面為抽象語法樹中所有的節點所共享;terminalexression為終結符表示式,實現與文法中的終結符相關聯的解釋操作;nonterminalexpression為非終結符表示式,為文法中的非終結符實現解釋操作,對文法中每一條規則r1、r2……rn都需要乙個具體的非終結符表示式類。
1抽象表示式是生成語法集合(語法樹)的關鍵,每個語法集合完成指定語法解析任務,它是通過遞迴呼叫的方式,最終由最小的語法單元進行解析完成。public
class
context
9public
void
setinput(string input)
12public
string getoutput()
15public
void
setoutput(string output)
1819 }
1通常,終結符表示式比較簡單,主要處理場景元素和資料的轉換。public
abstract
class
abstractexpression
1每個非終結符表示式都代表了乙個文法規則,並且每個文法規則都只關心自己周邊的文法規則的結果,因此這就產生了每個非終結符表示式呼叫自己周邊的非終結符表示式,然後最終、最小的文法規則就是終結符表示式。public
class terminalexpression extends
abstractexpression
78 }
1其中list為乙個語法容器,容納乙個具體的表示式。通常client是乙個封裝類,封裝的結果就是傳遞進來乙個規範語法檔案,解析器分析後產生結果並返回,避免了呼叫者與語法分析器的耦合關係。public
class nonterminalexpression extends
abstractexpression
78 }
1執行結果如下:public
class
client 15}
1617 }
我們現在通過直譯器模式來實現四則運算,如計算a+b的值。uml圖如下:
使用calculator建構函式傳參,並解析封裝。這裡根據棧的「先進後出」來安排運算的先後順序(主要用在乘除法,這裡只寫了加減法比較簡單)。以加法為例,calculator建構函式接收乙個表示式,然後把表示式轉換為char陣列,並判斷運算符號,如果是『+』則進行加法運算,把左邊的數(left變數)和右邊的數(right變數)加起來即可。
例如a+b-c這個表示式,根據for迴圈,首先被壓入棧中的是a元素生成的varexpression物件,然後判斷到加號時,把a元素的物件從棧中pop出來,與右邊的陣列b進行相加,而b是通過當前的陣列游標下移乙個單元格得來的(為了防止該元素被再次遍歷,通過++i的方式跳過下一遍歷)。減法運算同理。
1通過map鍵值對,使鍵對應公式引數,如a、b、c等,值為運算時取得的具體數值。public
class
calculator 31}
32this.expression =stack.pop();33}
3435
//計算
36public
int run(hashmapvar)
3940 }
1通過interpreter()方法從map中取之。public
abstract
class
expression
1這裡,每個運算符合都只和自己左右兩個數字有關係,但左右兩個數字有可能也是乙個解析的結果,無論何種型別,都是expression類的實現類。public
class varexpression extends
expression 89
@override
10public
int interpreter(hashmapvar)
1314 }
1public
class symbolexpression extends
expression
1011
@override
12public
int interpreter(hashmapvar)
1617 }
1public
class addexpression extends
symbolexpression 67
public
int interpreter(hashmapvar)
1011 }
1這裡就比較簡單了,通過getexpstr()方法獲取表示式,再通過getvalue()方法獲取值的對映,最後再例項化calculator類,通過run()方法獲取最終的運算結果。public
class subexpression extends
symbolexpression 67
public
int interpreter(hashmapvar)
1011 }
1運算結果如下:public
class
client 910
//獲得表示式
11public
static string getexpstr() throws
ioexception
1516
//獲得值對映
17public
static hashmapgetvalue(string expstr) throws
ioexception 27}
28}2930
return
map;31}
3233 }
簡說設計模式 直譯器模式
直譯器這個名詞想必大家都不會陌生,比如編譯原理中,乙個算術表示式通過詞法分析器形成詞法單元,而後這些詞法單元再通過語法分析器構建語法分析樹,最終形成一顆抽象的語法分析樹。諸如此類的例子也有很多,比如編譯器 正規表示式等等。如果一種特定型別的問題發生的頻率足夠高,那麼可能就值得將該問題的各個例項表述為...
簡說設計模式 職責鏈模式
從文字角度出發,我們可以先將關注點放在 鏈 字上,很容易聯想到鏈式結構,舉個生活中常見的例子,擊鼓傳花遊戲就是乙個很典型的鏈式結構,所有人形成一條鏈,相互傳遞。而從另乙個角度說,職責鏈就是所謂的多級結構,比如去醫院開具病假條,普通醫生只能開一天的證明,如果需要更多時常,則需將開具職責轉交到上級去,上...
簡說設計模式 享元模式
說到享元模式,第乙個想到的應該就是池技術了,string常量池 資料庫連線池 緩衝池等等都是享元模式的應用,所以說享元模式是池技術的重要實現方式。比如我們每次建立字串物件時,都需要建立乙個新的字串物件的話,記憶體開銷會很大,所以如果第一次建立了字串物件 adam 下次再建立相同的字串 adam 時,...