一、概念
1、解釋器模式的動(dòng)機(jī)
? 雖然目前計(jì)算機(jī)編程語(yǔ)言有好幾百種,但有時(shí)候我們還是希望能用一些簡(jiǎn)單的語(yǔ)言來(lái)實(shí)現(xiàn)一些特定的操作,我們只要向計(jì)算機(jī)輸入一個(gè)句子或文件,它就能夠按照預(yù)先定義的文法規(guī)則來(lái)對(duì)句子或文件進(jìn)行解釋?zhuān)瑥亩鴮?shí)現(xiàn)相應(yīng)的功能。例如提供一個(gè)簡(jiǎn)單的加法/減法解釋器,只要輸入一個(gè)加法/減法表達(dá)式,它就能夠計(jì)算出表達(dá)式結(jié)果,當(dāng)輸入字符串表達(dá)式為“1 + 2 + 3 – 4 + 1”時(shí),將輸出計(jì)算結(jié)果為3。
? 我們知道,像Java和OC等語(yǔ)言無(wú)法直接解釋類(lèi)似“1 + 2 + 3 – 4 + 1”這樣的字符串(如果直接作為數(shù)值表達(dá)式時(shí)可以解釋?zhuān)覀儽仨氉约憾x一套文法規(guī)則來(lái)實(shí)現(xiàn)對(duì)這些語(yǔ)句的解釋?zhuān)丛O(shè)計(jì)一個(gè)自定義語(yǔ)言,此時(shí)可以使用解釋器模式來(lái)實(shí)現(xiàn)自定義語(yǔ)言。
2、解釋器模式的定義
? 解釋器模式(Interpreter Pattern):定義一個(gè)語(yǔ)言的文法,并且建立一個(gè)解釋器來(lái)解釋該語(yǔ)言中的句子,這里的“語(yǔ)言”是指使用規(guī)定格式和語(yǔ)法的代碼。解釋器模式是一種類(lèi)行為型模式。
語(yǔ)言單位可以分為兩類(lèi):
1)終結(jié)符:也稱(chēng)為終結(jié)符表達(dá)式,它們是語(yǔ)言的最小組成單位,不能再進(jìn)行拆分;
2)非終結(jié)符:也稱(chēng)為非終結(jié)符表達(dá)式,它們都是一個(gè)完整的句子,包含一系列終結(jié)符或非終結(jié)符。
3、解釋器模式的4個(gè)角色
1)AbstractExpression(抽象表達(dá)式):在抽象表達(dá)式中聲明了抽象的解釋操作,它是所有終結(jié)符表達(dá)式和非終結(jié)符表達(dá)式的公共父類(lèi)。
2)TerminalExpression(終結(jié)符表達(dá)式):終結(jié)符表達(dá)式是抽象表達(dá)式的子類(lèi),它實(shí)現(xiàn)了與文法中的終結(jié)符相關(guān)聯(lián)的解釋操作,在句子中的每一個(gè)終結(jié)符都是該類(lèi)的一個(gè)實(shí)例。通常在一個(gè)解釋器模式中只有少數(shù)幾個(gè)終結(jié)符表達(dá)式類(lèi),它們的實(shí)例可以通過(guò)非終結(jié)符表達(dá)式組成較為復(fù)雜的句子。
3)NonterminalExpression(非終結(jié)符表達(dá)式):非終結(jié)符表達(dá)式也是抽象表達(dá)式的子類(lèi),它實(shí)現(xiàn)了文法中非終結(jié)符的解釋操作,由于在非終結(jié)符表達(dá)式中可以包含終結(jié)符表達(dá)式,也可以繼續(xù)包含非終結(jié)符表達(dá)式,因此其解釋操作一般通過(guò)遞歸的方式來(lái)完成。
4)Context(環(huán)境類(lèi)):環(huán)境類(lèi)又稱(chēng)為上下文類(lèi),它用于存儲(chǔ)解釋器之外的一些全局信息,通常它臨時(shí)存儲(chǔ)了需要解釋的語(yǔ)句。
4、結(jié)構(gòu)圖
二、示例
? 本示例是簡(jiǎn)單的將英語(yǔ)翻譯成中文,僅簡(jiǎn)單展示各個(gè)角色的使用,實(shí)際操作更復(fù)雜。
1)先創(chuàng)建一個(gè)Context環(huán)境類(lèi),里面存放著需要解釋的中英文;
2)然后創(chuàng)建AbstractExpression抽象表達(dá)式,有一個(gè)interpretWithContext()方法;
3)創(chuàng)建一個(gè)DirectionExpression類(lèi),英語(yǔ)方向解釋器,繼承自AbstractExpression類(lèi),表示終結(jié)符表達(dá)式;
4)最后創(chuàng)建AndExpression類(lèi),繼承自AbstractExpression類(lèi),表示非終結(jié)符表達(dá)式。
具體代碼如下:
Context類(lèi):
// 上下文
@interface Context : NSObject
- (NSString *)getObjectWithKey:(NSString *)key;
@end
@interface Context ()
@property(nonatomic, strong) NSMutableDictionary *dict;
@end
@implementation Context
- (instancetype)init
{
self = [super init];
if (self) {
_dict = [NSMutableDictionary dictionary];
[_dict setObject:@"向左" forKey:@"left"];
[_dict setObject:@"向右" forKey:@"right"];
[_dict setObject:@"向前" forKey:@"forward"];
[_dict setObject:@"向后" forKey:@"backward"];
}
return self;
}
- (NSString *)getObjectWithKey:(NSString *)key {
if ([self.dict.allKeys containsObject:key]) {
return [self.dict objectForKey:key];
}
return nil;
}
@end
AbstractExpression類(lèi):
@interface AbstractExpression : NSObject
- (NSString *)interpretWithContext:(Context *)context;
@end
@implementation AbstractExpression
- (NSString *)interpretWithContext:(Context *)context {
return nil;
}
@end
DirectionExpression類(lèi):
// 方向解釋?zhuān)航K結(jié)符表達(dá)式
@interface DirectionExpression : AbstractExpression
@property(nonatomic, copy) NSString *direction;
- (instancetype)initWithDirection:(NSString *)direction;
@end
@implementation DirectionExpression
- (instancetype)initWithDirection:(NSString *)direction {
self = [super init];
if (self) {
_direction = direction;
}
return self;
}
- (NSString *)interpretWithContext:(Context *)context {
NSString *result = [NSString stringWithFormat:@"[%@]", [context getObjectWithKey:self.direction]];
return result;
}
@end
AndExpression類(lèi):
// And解釋?zhuān)悍墙K結(jié)符表達(dá)式
@interface AndExpression : AbstractExpression
@property(nonatomic, strong) AbstractExpression *leftExpression;
@property(nonatomic, strong) AbstractExpression *rightExpression;
- (instancetype)initWithLeft:(AbstractExpression *)left right:(AbstractExpression *)right;
@end
@implementation AndExpression
- (instancetype)initWithLeft:(AbstractExpression *)left right:(AbstractExpression *)right {
self = [super init];
if (self) {
_leftExpression = left;
_rightExpression = right;
}
return self;
}
- (NSString *)interpretWithContext:(Context *)context {
NSString *leftStr = [self.leftExpression interpretWithContext:context];
NSString *rightStr = [self.rightExpression interpretWithContext:context];
NSString *result = [NSString stringWithFormat:@"%@ 再 %@", leftStr, rightStr];
return result;
}
@end
運(yùn)行代碼:
- (void)viewDidLoad {
[super viewDidLoad];
// 實(shí)現(xiàn)英語(yǔ)翻譯成中文的效果
Context *context = [Context new];
DirectionExpression *leftDir = [[DirectionExpression alloc] initWithDirection:@"left"];
DirectionExpression *rightDir = [[DirectionExpression alloc] initWithDirection:@"right"];
NSLog(@"終結(jié)符表達(dá)式:%@", [leftDir interpretWithContext:context]);
AndExpression *andExp = [[AndExpression alloc] initWithLeft:leftDir right:rightDir];
NSLog(@"非終結(jié)符表達(dá)式:%@", [andExp interpretWithContext:context]);
}
打印結(jié)果:
終結(jié)符表達(dá)式:[向左]
非終結(jié)符表達(dá)式:[向左] 再 [向右]
三、總結(jié)
? 解釋器模式為自定義語(yǔ)言的設(shè)計(jì)和實(shí)現(xiàn)提供了一種解決方案,它用于定義一組文法規(guī)則并通過(guò)這組文法規(guī)則來(lái)解釋語(yǔ)言中的句子。雖然解釋器模式的使用頻率不是特別高,但是它在正則表達(dá)式、XML文檔解釋等領(lǐng)域還是得到了廣泛使用。
1、優(yōu)點(diǎn)
1、易于改變和擴(kuò)展文法。由于在解釋器模式中使用類(lèi)來(lái)表示語(yǔ)言的文法規(guī)則,因此可以通過(guò)繼承等機(jī)制來(lái)改變或擴(kuò)展文法。
2、每一條文法規(guī)則都可以表示為一個(gè)類(lèi),因此可以方便地實(shí)現(xiàn)一個(gè)簡(jiǎn)單的語(yǔ)言。
3、增加新的解釋表達(dá)式較為方便。如果用戶(hù)需要增加新的解釋表達(dá)式只需要對(duì)應(yīng)增加一個(gè)新的終結(jié)符表達(dá)式或非終結(jié)符表達(dá)式類(lèi),原有表達(dá)式類(lèi)代碼無(wú)須修改,符合“開(kāi)閉原則”。
2、缺點(diǎn)
1、對(duì)于復(fù)雜文法難以維護(hù)。在解釋器模式中,每一條規(guī)則至少需要定義一個(gè)類(lèi),因此如果一個(gè)語(yǔ)言包含太多文法規(guī)則,類(lèi)的個(gè)數(shù)將會(huì)急劇增加,導(dǎo)致系統(tǒng)難以管理和維護(hù),此時(shí)可以考慮使用語(yǔ)法分析程序等方式來(lái)取代解釋器模式。
2、執(zhí)行效率較低。由于在解釋器模式中使用了大量的循環(huán)和遞歸調(diào)用,因此在解釋較為復(fù)雜的句子時(shí)其速度很慢,而且代碼的調(diào)試過(guò)程也比較麻煩。
3、適用場(chǎng)景
1、可以將一個(gè)需要解釋執(zhí)行的語(yǔ)言中的句子表示為一個(gè)抽象語(yǔ)法樹(shù),執(zhí)行效率不是關(guān)鍵問(wèn)題。
2、一些重復(fù)出現(xiàn)的問(wèn)題可以用一種簡(jiǎn)單的語(yǔ)言來(lái)進(jìn)行表達(dá)。
3、一個(gè)語(yǔ)言的文法較為簡(jiǎn)單。
4、iOS應(yīng)用舉例
? 比如判斷郵件地址、電話(huà)號(hào)碼、證件號(hào)碼是否是正確的正則表達(dá)式,就是應(yīng)用了解釋器模式。
Demo地址:iOS-Design-Patterns