1 意圖
給定一個(gè)語(yǔ)言,定義它的文法的一種表示瞬项,并定義一個(gè)解釋器蠕嫁,這個(gè)解釋器使用該表示來(lái)解釋語(yǔ)言中的句子地梨。
2 動(dòng)機(jī)
如果一種特定類(lèi)型的問(wèn)題發(fā)生的頻率足夠高帝璧,那么可能就值得將問(wèn)題的各個(gè)實(shí)例表述為一個(gè)簡(jiǎn)單語(yǔ)言中的句子。這樣就可以構(gòu)建一個(gè)解釋器湿刽,該解釋器通過(guò)解釋這些句子來(lái)解決該問(wèn)題。
例如褐耳,搜索匹配一個(gè)模式的字符串是一個(gè)常見(jiàn)的問(wèn)題诈闺。正則表達(dá)式是描述字符串模式的一種標(biāo)準(zhǔn)語(yǔ)言。與其為每一個(gè)的模式都構(gòu)造一個(gè)特定的算法铃芦,不如使用一種通用的搜索算法來(lái)解釋執(zhí)行一個(gè)正則表達(dá)式雅镊,該正則表達(dá)式定義了待匹配字符串的集合。
解釋器模式描述了如何為簡(jiǎn)單的語(yǔ)言定義一個(gè)文法刃滓,如何在該語(yǔ)言中表示一個(gè)句子仁烹,以及如何解釋這些句子。在上面的例子中咧虎,本設(shè)計(jì)模式描述了如何為正則表達(dá)式定義一個(gè)文法卓缰,如果表示一個(gè)特定的正則表達(dá)式,以及如何解釋這個(gè)正則表達(dá)式。
考慮以下文法定義正則表達(dá)式:
解釋器模式使用類(lèi)來(lái)表示每一條文法規(guī)則征唬,在規(guī)則右邊的符號(hào)是這些類(lèi)的實(shí)例變量捌显。上面的文法用5個(gè)類(lèi)表示,一個(gè)抽象類(lèi)RegularExpression和它的四個(gè)子類(lèi)LiteralExpression总寒、AlternationExpression扶歪、SequenceExpression和RepetitionExpression后三個(gè)類(lèi)定義的變量代表子表達(dá)式。
每個(gè)用這個(gè)文法定義的正則表達(dá)式都被表示為一個(gè)由這些類(lèi)的實(shí)例構(gòu)成的抽象語(yǔ)法樹(shù)摄闸。
例如, 抽象語(yǔ)法樹(shù):
表示正則表達(dá)式:
raining & (dogs | cats) *
如果我們?yōu)镽egularExpression的每一子類(lèi)都定義解釋 ( Interpret )操作善镰,那么就得到了為這些正則表達(dá)式的一個(gè)解釋器。解釋器將該表達(dá)式的上下文做為一個(gè)參數(shù)年枕。上下文包含輸入字符串和關(guān)于目前它已有多少已經(jīng)被匹配等信息炫欺。為匹配輸入字符串的下一部分,每一RegularExpression的子類(lèi)都在當(dāng)前上下文的基礎(chǔ)上實(shí)現(xiàn)解釋操作 (Interpret)画切。例如,
- LiteralExpression將檢查輸入是否匹配它定義的字 ( literal );
- AlternationExpression將檢查輸入是否匹配它的任意一個(gè)選擇項(xiàng);
- RepetitionExpression將檢查輸入是否含有多個(gè)它所重復(fù)的表達(dá)式竣稽。
3 適用性
當(dāng)有一個(gè)語(yǔ)言需要解釋執(zhí)行 , 并且你可將該語(yǔ)言中的句子表示為一個(gè)抽象語(yǔ)法樹(shù)時(shí),可使
用解釋器模式霍弹。而當(dāng)存在以下情況時(shí)該模式效果最好:
- 該文法簡(jiǎn)單對(duì)于復(fù)雜的文法 , 文法的類(lèi)層次變得龐大而無(wú)法管理毫别。此時(shí)語(yǔ)法分析程序生
成器這樣的工具是更好的選擇。它們無(wú)需構(gòu)建抽象語(yǔ)法樹(shù)即可解釋表達(dá)式 , 這樣可以節(jié)
省空間而且還可能節(jié)省時(shí)間典格。 - 效率不是一個(gè)關(guān)鍵問(wèn)題最高效的解釋器通常不是通過(guò)直接解釋語(yǔ)法分析樹(shù)實(shí)現(xiàn)的 , 而是
首先將它們轉(zhuǎn)換成另一種形式岛宦。例如,正則表達(dá)式通常被轉(zhuǎn)換成狀態(tài)機(jī)耍缴。但即使在這種
情況下, 轉(zhuǎn)換器仍可用解釋器模式實(shí)現(xiàn), 該模式仍是有用的砾肺。
4 結(jié)構(gòu)
5 參與者
- AbstractExpression(抽象表達(dá)式,如RegularExpression )
——聲明一個(gè)抽象的解釋操作防嗡,這個(gè)接口為抽象語(yǔ)法中所有的節(jié)點(diǎn)所共享变汪; - TerminalExpression(終結(jié)符表達(dá)式,如LiteralExpression)
——實(shí)現(xiàn)與文法中的終結(jié)符相關(guān)聯(lián)的解釋操作蚁趁;
——一個(gè)句子的每個(gè)終結(jié)符需要該類(lèi)的一個(gè)實(shí)例裙盾。 - NonterminalExpression (非終結(jié)符表達(dá)式,如AlternationExpression,RepetitionExpression,SequenceExpressions)
——對(duì)文法中的每一條規(guī)則 R ::= R1R2. . . Rn都需要一個(gè)NonterminalExpression類(lèi)他嫡;
——為從R1到Rn的每個(gè)符號(hào)都維護(hù)一個(gè)AbstractExpression類(lèi)型的實(shí)例變量番官;
——為文法中的非終結(jié)符實(shí)現(xiàn)解釋 ( Interpret )操作。解釋( Interpret )一般要遞歸地調(diào)用表示R1到Rn的那些對(duì)象的解釋操作钢属。
6 協(xié)作
- Client構(gòu)建(或被給定)一個(gè)句子徘熔,它是NonterminalExpression和TerminalExpression的實(shí)例裝配而成。
6 協(xié)作
- 1 Client構(gòu)建(或被給定)一個(gè)句子淆党,它是由NonterminalExpression和TerminalExpression的實(shí)例裝配而成的一個(gè)抽象語(yǔ)法樹(shù)酷师,然后初始化上下文并調(diào)用解釋操作祟峦。
- 2 每一非終結(jié)符表達(dá)式節(jié)點(diǎn)定義相應(yīng)表達(dá)式的解釋操作狸棍,而各終極符表達(dá)式的解釋操作構(gòu)成了遞歸的基礎(chǔ)桂敛。
- 3 每一節(jié)點(diǎn)的解釋操作通過(guò)上下文來(lái)存儲(chǔ)和訪問(wèn)解釋器的狀態(tài)赡矢。
7 效果
解釋器模式有下列的優(yōu)點(diǎn)和不足:
- 1 易于改變和擴(kuò)展文法 因?yàn)樵撃J绞褂妙?lèi)來(lái)表示文法規(guī)則 , 你可使用繼承來(lái)改變或擴(kuò)展該文法。已有的表達(dá)式可被增量式地改變 ,而新的表達(dá)式可定義為舊表達(dá)式的變體饱须。
- 2 也易于實(shí)現(xiàn)文法 定義抽象語(yǔ)法樹(shù)中各個(gè)節(jié)點(diǎn)的類(lèi)的實(shí)現(xiàn)大體類(lèi)似域醇。這些類(lèi)易于直接編寫(xiě),通常它們也可以用一個(gè)編譯器或語(yǔ)法分析程序生成器自動(dòng)生成蓉媳。
- 3 復(fù)雜的文法難以維護(hù) 解釋器模式為文法中的每一條規(guī)則至少定義了一個(gè)類(lèi)譬挚,因此包含許多規(guī)則的文法可能難以管理和維護(hù)±疑耄可應(yīng)用其它的設(shè)計(jì)模式來(lái)緩解這一問(wèn)題减宣。但當(dāng)文法非常復(fù)雜時(shí),其它技術(shù)如語(yǔ)法分析程序或編譯生成器更為合適玩荠。
- 4 增加了新的解釋表達(dá)式的方式 解釋器模式使得實(shí)現(xiàn)新表達(dá)式“計(jì)算”變得容易漆腌。 例如,你可以在表達(dá)式類(lèi)上定義一個(gè)新的操作以支持優(yōu)美打印或表達(dá)式的類(lèi)型檢查。如果你經(jīng)常創(chuàng)建新的解釋表達(dá)式的方式, 那么可以考慮使用Visitor(5.11)模式以避免修改這些代表文法的類(lèi)阶冈。
8 實(shí)現(xiàn)
Interpreter和Composite(4 . 3)模式在實(shí)現(xiàn)上有許多相通的地方闷尿。下面是Interpreter所要考慮的一些特殊問(wèn)題:
- 1 創(chuàng)建抽象語(yǔ)法樹(shù)解釋器模式并未解釋如何創(chuàng)建一個(gè)抽象的語(yǔ)法樹(shù)。換言之 , 它不涉及語(yǔ)法分析女坑。抽象語(yǔ)法樹(shù)可用一個(gè)表驅(qū)動(dòng)的語(yǔ)法分析程序來(lái)生成填具,也可用手寫(xiě)的 (通常為遞歸下降法) 語(yǔ)法分析程序創(chuàng)建,或直接由Client提供匆骗;
- 2 定義解釋操作 并不一定要在表達(dá)式類(lèi)中定義解釋操作劳景。如果經(jīng)常要?jiǎng)?chuàng)建一種新的解釋器, 那么使用Visitor(5 . 11)模式將解釋放入一個(gè)獨(dú)立的 “訪問(wèn)者” 對(duì)象更好一些。例如,一個(gè)程序設(shè)計(jì)語(yǔ)言的會(huì)有許多在抽象語(yǔ)法樹(shù)上的操作碉就,比如類(lèi)型檢查盟广、優(yōu)化、代碼生成瓮钥,等等筋量。恰當(dāng)?shù)淖龇ㄊ鞘褂靡粋€(gè)訪問(wèn)者以避免在每一個(gè)類(lèi)上都定義這些操作。
- 3 與Flyweight模式共享終結(jié)符 在一些文法中, 一個(gè)句子可能多次出現(xiàn)同一個(gè)終結(jié)符骏庸。此時(shí)最好共享那個(gè)符號(hào)的單個(gè)拷貝。計(jì)算機(jī)程序的文法是很好的例子 — 每個(gè)程序變量在整個(gè)代碼中將會(huì)出現(xiàn)多次年叮。在動(dòng)機(jī)一節(jié)的例子中 , 一個(gè)句子中終結(jié)符dog (由LiteralExpression類(lèi)描述)也可出現(xiàn)多次具被。終結(jié)節(jié)點(diǎn)通常不存儲(chǔ)關(guān)于它們?cè)诔橄笳Z(yǔ)法樹(shù)中位置的信息。在解釋過(guò)程中只损,任何它們所
需要的上下文信息都由父節(jié)點(diǎn)傳遞給它們一姿。因此在共享的 (內(nèi)部的)狀態(tài)和傳入的(外部的)狀態(tài)區(qū)分得很明確, 這就用到了Flyweight(4 . 6)模式七咧。