一、概念
1、解释器模式的动机
虽然目前计算机编程语言有好几百种,但有时候我们还是希望能用一些简单的语言来实现一些特定的操作,我们只要向计算机输入一个句子或文件,它就能够按照预先定义的文法规则来对句子或文件进行解释,从而实现相应的功能。例如提供一个简单的加法/减法解释器,只要输入一个加法/减法表达式,它就能够计算出表达式结果,当输入字符串表达式为“1 + 2 + 3 – 4 + 1”时,将输出计算结果为3。
我们知道,像Java和OC等语言无法直接解释类似“1 + 2 + 3 – 4 + 1”这样的字符串(如果直接作为数值表达式时可以解释),我们必须自己定义一套文法规则来实现对这些语句的解释,即设计一个自定义语言,此时可以使用解释器模式来实现自定义语言。
2、解释器模式的定义
解释器模式(Interpreter Pattern):定义一个语言的文法,并且建立一个解释器来解释该语言中的句子,这里的“语言”是指使用规定格式和语法的代码。解释器模式是一种类行为型模式。
语言单位可以分为两类:
1)终结符:也称为终结符表达式,它们是语言的最小组成单位,不能再进行拆分;
2)非终结符:也称为非终结符表达式,它们都是一个完整的句子,包含一系列终结符或非终结符。
3、解释器模式的4个角色
1)AbstractExpression(抽象表达式):在抽象表达式中声明了抽象的解释操作,它是所有终结符表达式和非终结符表达式的公共父类。
2)TerminalExpression(终结符表达式):终结符表达式是抽象表达式的子类,它实现了与文法中的终结符相关联的解释操作,在句子中的每一个终结符都是该类的一个实例。通常在一个解释器模式中只有少数几个终结符表达式类,它们的实例可以通过非终结符表达式组成较为复杂的句子。
3)NonterminalExpression(非终结符表达式):非终结符表达式也是抽象表达式的子类,它实现了文法中非终结符的解释操作,由于在非终结符表达式中可以包含终结符表达式,也可以继续包含非终结符表达式,因此其解释操作一般通过递归的方式来完成。
4)Context(环境类):环境类又称为上下文类,它用于存储解释器之外的一些全局信息,通常它临时存储了需要解释的语句。
4、结构图
二、示例
本示例是简单的将英语翻译成中文,仅简单展示各个角色的使用,实际操作更复杂。
1)先创建一个Context环境类,里面存放着需要解释的中英文;
2)然后创建AbstractExpression抽象表达式,有一个interpretWithContext()方法;
3)创建一个DirectionExpression类,英语方向解释器,继承自AbstractExpression类,表示终结符表达式;
4)最后创建AndExpression类,继承自AbstractExpression类,表示非终结符表达式。
具体代码如下:
Context类:
// 上下文
@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类:
@interface AbstractExpression : NSObject
- (NSString *)interpretWithContext:(Context *)context;
@end
@implementation AbstractExpression
- (NSString *)interpretWithContext:(Context *)context {
return nil;
}
@end
DirectionExpression类:
// 方向解释:终结符表达式
@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类:
// And解释:非终结符表达式
@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
运行代码:
- (void)viewDidLoad {
[super viewDidLoad];
// 实现英语翻译成中文的效果
Context *context = [Context new];
DirectionExpression *leftDir = [[DirectionExpression alloc] initWithDirection:@"left"];
DirectionExpression *rightDir = [[DirectionExpression alloc] initWithDirection:@"right"];
NSLog(@"终结符表达式:%@", [leftDir interpretWithContext:context]);
AndExpression *andExp = [[AndExpression alloc] initWithLeft:leftDir right:rightDir];
NSLog(@"非终结符表达式:%@", [andExp interpretWithContext:context]);
}
打印结果:
终结符表达式:[向左]
非终结符表达式:[向左] 再 [向右]
三、总结
解释器模式为自定义语言的设计和实现提供了一种解决方案,它用于定义一组文法规则并通过这组文法规则来解释语言中的句子。虽然解释器模式的使用频率不是特别高,但是它在正则表达式、XML文档解释等领域还是得到了广泛使用。
1、优点
1、易于改变和扩展文法。由于在解释器模式中使用类来表示语言的文法规则,因此可以通过继承等机制来改变或扩展文法。
2、每一条文法规则都可以表示为一个类,因此可以方便地实现一个简单的语言。
3、增加新的解释表达式较为方便。如果用户需要增加新的解释表达式只需要对应增加一个新的终结符表达式或非终结符表达式类,原有表达式类代码无须修改,符合“开闭原则”。
2、缺点
1、对于复杂文法难以维护。在解释器模式中,每一条规则至少需要定义一个类,因此如果一个语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护,此时可以考虑使用语法分析程序等方式来取代解释器模式。
2、执行效率较低。由于在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦。
3、适用场景
1、可以将一个需要解释执行的语言中的句子表示为一个抽象语法树,执行效率不是关键问题。
2、一些重复出现的问题可以用一种简单的语言来进行表达。
3、一个语言的文法较为简单。
4、iOS应用举例
比如判断邮件地址、电话号码、证件号码是否是正确的正则表达式,就是应用了解释器模式。
Demo地址:iOS-Design-Patterns