iOS 设计模式之十六(解释器模式)

一、概念

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容