.Net行為型設(shè)計(jì)模式之解釋器模式(Interpreter)
一、動(dòng)機(jī)(Motivate)
在軟件構(gòu)建過程中,如果某一特定領(lǐng)域的問題比較復(fù)雜,類似的模式不斷重復(fù)出現(xiàn),如果使用普通的編程方式來實(shí)現(xiàn)將面臨非常頻繁的變化。在這種情況下,將特定領(lǐng)域的問題表達(dá)為某種語(yǔ)法規(guī)則下的句子,然后構(gòu)建一個(gè)解釋器來解釋這樣的句子,從而達(dá)到解決問題的目的。
二、意圖(Intent)
給定一個(gè)語(yǔ)言,定義它的文法的一種表示,并定義一種解釋器,這個(gè)解釋器使用該表示來解釋語(yǔ)言中的句子。 ——《設(shè)計(jì)模式》GoF
三、結(jié)構(gòu)圖(Structure)

四、模式的組成
可以看出,在解釋器模式的結(jié)構(gòu)圖有以下角色:
(1)、抽象表達(dá)式(AbstractExpression):定義解釋器的接口,約定解釋器的解釋操作。其中的Interpret接口,正如其名字那樣,它是專門用來解釋該解釋器所要實(shí)現(xiàn)的功能。
(2)、終結(jié)符表達(dá)式(Terminal Expression):實(shí)現(xiàn)了抽象表達(dá)式角色所要求的接口,主要是一個(gè)interpret()方法;文法中的每一個(gè)終結(jié)符都有一個(gè)具體終結(jié)表達(dá)式與之相對(duì)應(yīng)。比如有一個(gè)簡(jiǎn)單的公式R=R1+R2,在里面R1和R2就是終結(jié)符,對(duì)應(yīng)的解析R1和R2的解釋器就是終結(jié)符表達(dá)式。
(3)、非終結(jié)符表達(dá)式(Nonterminal Expression):文法中的每一條規(guī)則都需要一個(gè)具體的非終結(jié)符表達(dá)式,非終結(jié)符表達(dá)式一般是文法中的運(yùn)算符或者其他關(guān)鍵字,比如公式R=R1+R2中,“+”就是非終結(jié)符,解析“+”的解釋器就是一個(gè)非終結(jié)符表達(dá)式。
(4)、環(huán)境角色(Context):這個(gè)角色的任務(wù)一般是用來存放文法中各個(gè)終結(jié)符所對(duì)應(yīng)的具體值,比如R=R1+R2,我們給R1賦值100,給R2賦值200。這些信息需要存放到環(huán)境角色中,很多情況下我們使用Map來充當(dāng)環(huán)境角色就足夠了。
(5)、客戶端(Client):指的是使用解釋器的客戶端,通常在這里將按照語(yǔ)言的語(yǔ)法做的表達(dá)式轉(zhuǎn)換成使用解釋器對(duì)象描述的抽象語(yǔ)法樹,然后調(diào)用解釋操作。
五、解釋器模式的代碼實(shí)現(xiàn)
在很多場(chǎng)合都需要把數(shù)字轉(zhuǎn)換成中文,我們就可以使用解釋器來實(shí)現(xiàn)該功能,把給定的數(shù)字解釋成符合語(yǔ)法規(guī)范的漢字表示法。實(shí)現(xiàn)代碼如下:
static void Main(string[] args)
{
string roman = "五億七千三百零二萬(wàn)六千四百五十二";
//分解:((五)億)((七千)(三百)(零)(二)萬(wàn))
//((六千)(四百)(五十)(二))
Context context = new Context(roman);
ArrayList tree = new ArrayList();
tree.Add(new GeExpression());
tree.Add(new ShiExpression());
tree.Add(new BaiExpression());
tree.Add(new QianExpression());
tree.Add(new WanExpression());
tree.Add(new YiExpression());
foreach (Expression exp in tree)
{
exp.Interpreter(context);
}
Console.Write(context.Data);
}
// 抽象表達(dá)式
public abstract class Expression
{
protected Dictionary<string, int> table = new Dictionary<string, int>(9);
protected Expression()
{
table.Add("一", 1);
table.Add("二", 2);
table.Add("三", 3);
table.Add("四", 4);
table.Add("五", 5);
table.Add("六", 6);
table.Add("七", 7);
table.Add("八", 8);
table.Add("九", 9);
}
public virtual void Interpreter(Context context)
{
if (context.Statement.Length == 0)
{
return;
}
foreach (string key in table.Keys)
{
int value = table[key];
if (context.Statement.EndsWith(key + GetPostFix()))
{
context.Data += value * this.Multiplier();
context.Statement = context.Statement.Substring(0, context.Statement.Length - this.GetLength());
}
if (context.Statement.EndsWith("零"))
{
context.Statement = context.Statement.Substring(0, context.Statement.Length - 1);
}
}
}
public abstract string GetPostFix();
public abstract int Multiplier();
//這個(gè)可以通用,但是對(duì)于個(gè)位數(shù)字例外,所以用虛方法
public virtual int GetLength()
{
return this.GetPostFix().Length + 1;
}
}
//個(gè)位表達(dá)式
public sealed class GeExpression : Expression
{
public override string GetPostFix()
{
return "";
}
public override int Multiplier()
{
return 1;
}
public override int GetLength()
{
return 1;
}
}
//十位表達(dá)式
public sealed class ShiExpression : Expression
{
public override string GetPostFix()
{
return "十";
}
public override int Multiplier()
{
return 10;
}
}
//百位表達(dá)式
public sealed class BaiExpression : Expression
{
public override string GetPostFix()
{
return "百";
}
public override int Multiplier()
{
return 100;
}
}
//千位表達(dá)式
public sealed class QianExpression : Expression
{
public override string GetPostFix()
{
return "千";
}
public override int Multiplier()
{
return 1000;
}
}
//萬(wàn)位表達(dá)式
public sealed class WanExpression : Expression
{
public override string GetPostFix()
{
return "萬(wàn)";
}
public override int Multiplier()
{
return 10000;
}
public override void Interpreter(Context context)
{
if (context.Statement.Length == 0)
{
return;
}
ArrayList tree = new ArrayList();
tree.Add(new GeExpression());
tree.Add(new ShiExpression());
tree.Add(new BaiExpression());
tree.Add(new QianExpression());
foreach (string key in table.Keys)
{
if (context.Statement.EndsWith(GetPostFix()))
{
int temp = context.Data;
context.Data = 0;
context.Statement = context.Statement.Substring(0, context.Statement.Length - this.GetLength());
foreach (Expression exp in tree)
{
exp.Interpreter(context);
}
context.Data = temp + context.Data * this.Multiplier();
}
}
}
}
//億位表達(dá)式
public sealed class YiExpression : Expression
{
public override string GetPostFix()
{
return "億";
}
public override int Multiplier()
{
return 100000000;
}
public override void Interpreter(Context context)
{
ArrayList tree = new ArrayList();
tree.Add(new GeExpression());
tree.Add(new ShiExpression());
tree.Add(new BaiExpression());
tree.Add(new QianExpression());
foreach (string key in table.Keys)
{
if (context.Statement.EndsWith(GetPostFix()))
{
int temp = context.Data;
context.Data = 0;
context.Statement = context.Statement.Substring(0, context.Statement.Length - this.GetLength());
foreach (Expression exp in tree)
{
exp.Interpreter(context);
}
context.Data = temp + context.Data * this.Multiplier();
}
}
}
}
//環(huán)境上下文
public sealed class Context
{
private string _statement;
private int _data;
public Context(string statement)
{
this._statement = statement;
}
public string Statement
{
get { return this._statement; }
set { this._statement = value; }
}
public int Data
{
get { return this._data; }
set { this._data = value; }
}
}六、解釋器模式的實(shí)現(xiàn)要點(diǎn):
使用Interpreter模式來表示文法規(guī)則,從而可以使用面向?qū)ο蠹记煞奖愕?ldquo;擴(kuò)展”文法。
Interpreter模式比較適合簡(jiǎn)單的文法表示,對(duì)于復(fù)雜的文法表示,Interpreter模式會(huì)產(chǎn)生比較大的類層次結(jié)構(gòu),需要求助于語(yǔ)法分析生成器這樣的標(biāo)準(zhǔn)工具。
1、解釋器模式的主要優(yōu)點(diǎn)有:
1】、易于改變和擴(kuò)展文法。
2】、每一條文法規(guī)則都可以表示為一個(gè)類,因此可以方便地實(shí)現(xiàn)一個(gè)簡(jiǎn)單的語(yǔ)言。
3】、實(shí)現(xiàn)文法較為容易。在抽象語(yǔ)法樹中每一個(gè)表達(dá)式節(jié)點(diǎn)類的實(shí)現(xiàn)方式都是相似的,這些類的代碼編寫都不會(huì)特別復(fù)雜,還可以通過一些工具自動(dòng)生成節(jié)點(diǎn)類代碼。
4】、增加新的解釋表達(dá)式較為方便。如果用戶需要增加新的解釋表達(dá)式只需要對(duì)應(yīng)增加一個(gè)新的終結(jié)符表達(dá)式或非終結(jié)符表達(dá)式類,原有表達(dá)式類代碼無(wú)須修改,符合“開閉原則”
2、解釋器模式的主要缺點(diǎn)有:
1】、對(duì)于復(fù)雜文法難以維護(hù)。在解釋器模式中,每一條規(guī)則至少需要定義一個(gè)類,因此如果一個(gè)語(yǔ)言包含太多文法規(guī)則,類的個(gè)數(shù)將會(huì)急劇增加,導(dǎo)致系統(tǒng)難以管理和維護(hù),此時(shí)可以考慮使用語(yǔ)法分析程序等方式來取代解釋器模式。
2】、執(zhí)行效率較低。由于在解釋器模式中使用了大量的循環(huán)和遞歸調(diào)用,因此在解釋較為復(fù)雜的句子時(shí)其速度很慢,而且代碼的調(diào)試過程也比較麻煩。
3、在下面的情況下可以考慮使用解釋器模式:
Interpreter模式的應(yīng)用場(chǎng)合是Interpreter模式應(yīng)用中的難點(diǎn),只有滿足“業(yè)務(wù)規(guī)則頻繁變化,且類似的模式不斷重復(fù)出現(xiàn),并且容易抽象為語(yǔ)法規(guī)則的問題”才適合使用Interpreter模式。
1】、當(dāng)一個(gè)語(yǔ)言需要解釋執(zhí)行,并可以將該語(yǔ)言中的句子表示為一個(gè)抽象語(yǔ)法樹的時(shí)候,可以考慮使用解釋器模式(如XML文檔解釋、正則表達(dá)式等領(lǐng)域)
2】、一些重復(fù)出現(xiàn)的問題可以用一種簡(jiǎn)單的語(yǔ)言來進(jìn)行表達(dá)。
3】、一個(gè)語(yǔ)言的文法較為簡(jiǎn)單.
4】、當(dāng)執(zhí)行效率不是關(guān)鍵和主要關(guān)心的問題時(shí)可考慮解釋器模式(注:高效的解釋器通常不是通過直接解釋抽象語(yǔ)法樹來實(shí)現(xiàn)的,而是需要將它們轉(zhuǎn)換成其他形式,使用解釋器模式的執(zhí)行效率并不高。)
七、.NET 解釋器模式的實(shí)現(xiàn)
正則表達(dá)式就是一個(gè)典型的解釋器。ASP.NET中,把a(bǔ)spx文件轉(zhuǎn)化為dll時(shí),會(huì)對(duì)html語(yǔ)言進(jìn)行處理,這個(gè)處理過程也包含了解釋器的模式在里面。Interpreter模式其實(shí)有Composite模式的影子,但它們解決的問題是不一樣的。
八、總結(jié)
(1)解釋器和組合模式:這兩種可以組合使用,一般非終結(jié)符解釋器相當(dāng)于組合模式中的組合對(duì)象,終結(jié)符解釋器相當(dāng)于葉子對(duì)象。
(2)解釋器模式和迭代器模式:由于解釋器模式通常使用組合模式來實(shí)現(xiàn),因此在遍歷整個(gè)對(duì)象結(jié)構(gòu)時(shí),可以使用迭代器模式。
(3)解釋器模式和享元模式:在使用解釋器模式的時(shí)候,可能會(huì)造成多個(gè)細(xì)粒度對(duì)象,如各式各樣的終結(jié)符解釋器,而這些終結(jié)符解釋器對(duì)不同的表達(dá)式來說是一樣的,是可以共用的,因此可以引入享元模式來共享這些對(duì)象。
(4)解釋器模式和訪問者模式:在解釋器模式中,語(yǔ)法規(guī)則和解釋器對(duì)象是有對(duì)應(yīng)關(guān)系的。語(yǔ)法規(guī)則的變動(dòng)意味著功能的變化。自然會(huì)導(dǎo)致使用不同的解釋器對(duì)象;而且一個(gè)語(yǔ)法規(guī)由可以被不同的解釋器解釋執(zhí)行。因此在構(gòu)建抽象語(yǔ)法樹的時(shí)候,如果每個(gè)節(jié)點(diǎn)所對(duì)應(yīng)的解釋器對(duì)象是固定的,這意味著該節(jié)點(diǎn)對(duì)應(yīng)的功能是固定的,那么就不得不根據(jù)需要來構(gòu)建不同的抽象語(yǔ)法樹。為了讓構(gòu)建的抽象語(yǔ)法樹較為通用,那就要求解釋器的功能不要那么固定,要能很方便地改變解釋器的功能,這個(gè)時(shí)候就變成了如何能夠很方便地更改樹形結(jié)構(gòu)中節(jié)點(diǎn)對(duì)象的功能了,訪問者模式可以很好的實(shí)現(xiàn)這個(gè)功能。
到此這篇關(guān)于.Net行為型設(shè)計(jì)模式之解釋器模式(Interpreter)的文章就介紹到這了。希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- .Net行為型設(shè)計(jì)模式之備忘錄模式(Memento)
- .Net行為型設(shè)計(jì)模式之訪問者模式(Visitor)
- .Net行為型設(shè)計(jì)模式之職責(zé)鏈模式(Chain of Responsibility)
- .Net行為型設(shè)計(jì)模式之策略模式(Stragety)
- .Net行為型設(shè)計(jì)模式之狀態(tài)模式(State)
- .Net行為型設(shè)計(jì)模式之中介者模式(Mediator)
- .Net行為型設(shè)計(jì)模式之觀察者模式(Observer)
- .Net行為型設(shè)計(jì)模式之迭代器模式(Iterator)
- .Net行為型設(shè)計(jì)模式之命令模式(Command)
- .Net行為型設(shè)計(jì)模式之模板方法模式(Template?Method)
相關(guān)文章
ASP.Net前臺(tái)調(diào)用后臺(tái)變量的方法
asp.net下前臺(tái)數(shù)據(jù)的調(diào)用輸出方法,需要的朋友可以參考下。2010-07-07
asp.net Ajax之無(wú)刷新評(píng)論介紹
asp.net Ajax之無(wú)刷新評(píng)論介紹;需要的朋友可以參考下2012-11-11
Asp.Net 不同的OnClick事件區(qū)別小結(jié)(onserverclick,onclientclick)
下面以 HTML的按鈕( system.web.ui.htmlcontrols ) 和 ASP.NET服務(wù)端按鈕 ( system.web.ui.webcontrols ) 為例2012-05-05
ASP.NET?Core?MVC中Required與BindRequired用法與區(qū)別介紹
這篇文章介紹了ASP.NET?Core?MVC中Required與BindRequired用法與區(qū)別,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-02-02
ASP.NET Core基礎(chǔ)之請(qǐng)求處理管道
這篇文章介紹了ASP.NET Core的請(qǐng)求處理管道,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-02-02

