行为型设计模式-解释器模式

定义

给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。

文法:即语法规则。在解释器模式中每一个语法都将对应一个解释器对象,用来处理相应的语法规则。它对于扩展、改变文法以及增加新的文法规则都很方便。

解释器模式描述了如何为简单的语言定义一个文法,如何在该语言中表示一个句子,以及如何解释这些句子。

在解释器模式中可以通过一种称之为抽象语法树(Abstract Syntax Tree, AST)的图形方式来直观地表示语言的构成,每一棵抽象语法树对应一个语言实例


角色

  • AbstractExpression: 抽象表达式,声明一个抽象的解释操作父类,定义一个抽象的解释方法,具体的实现由子类解释器完成/
  • TerminalExpression: 终结符表达式,实现文法中与终结符有关的解释操作,文法中每一个终结符都有一个具体的终结表达式与之对应
  • NonterminalExpression: 非终结符表达式,实现文法中与非终结符有关的解释操作
  • Context: 上下文环境类,包含解释器之外的全局信息
  • Client: 客户端,解析表达式,构建抽象语法树,执行具体的解释操作等.

解释器UML 图

解释器UML 图

场景模拟

四则运算

四则运算简单代码

只实现了加法和减法

#import <Foundation/Foundation.h>

@protocol ArithmeticExpression <NSObject>
-(int)interptet;
@end

#import <Foundation/Foundation.h>
#import "ArithmeticExpression.h"
@interface NumExpression : NSObject<ArithmeticExpression>
- (instancetype)initWithNum:(int)num;
@end

#import "NumExpression.h"
@interface NumExpression()
@property (nonatomic,assign) int  num;
@end
@implementation NumExpression
- (instancetype)initWithNum:(int)num
{
    self = [super init];
    if (self) {
        self.num = num;
    }
    return self;
}
-(int)interptet{
    return self.num;
}

@end


#import <Foundation/Foundation.h>
#import "ArithmeticExpression.h"
@interface OperatorExpression : NSObject<ArithmeticExpression>
{
    @protected
    id<ArithmeticExpression> _mArithmeticExpression1;
    id<ArithmeticExpression> _mArithmeticExpression2;
}

-(void)OperatorExpression:(id<ArithmeticExpression>)mArithmeticExpression1 :(id<ArithmeticExpression>)mArithmeticExpression2;
@end

import "OperatorExpression.h"

@interface OperatorExpression()

@end

@implementation OperatorExpression
-(void)OperatorExpression:(id<ArithmeticExpression>)mArithmeticExpression1 :(id<ArithmeticExpression>)mArithmeticExpression2{
    _mArithmeticExpression1 = mArithmeticExpression1;
    _mArithmeticExpression2 = mArithmeticExpression2;
}
#import <Foundation/Foundation.h>
#import "ArithmeticExpression.h"
#import "NumExpression.h"
#import "AdditionExpression.h"
#import "ReduceExpression.h"
@interface Calculator : NSObject
-(void)calculator:(NSString *)expression;
-(int)calculator;
@end

#import "Calculator.h"
@interface Calculator()
@property (nonatomic,strong) NSMutableArray *mArithmeticExpressionStack;
@end

@implementation Calculator

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.mArithmeticExpressionStack = [NSMutableArray new];
    }
    return self;
}

-(void)calculator:(NSString *)expression{
    id<ArithmeticExpression> one;
    id <ArithmeticExpression> two;
    
    while (1) {
        if ([expression hasPrefix:@"+"]) {
            one = [self.mArithmeticExpressionStack lastObject];
            [self.mArithmeticExpressionStack removeLastObject];
            expression =  [expression substringFromIndex:1];
            int b =[expression intValue];
            expression = [self getExpression:expression];
            two = [[NumExpression alloc]initWithNum:b];
            AdditionExpression * add= [AdditionExpression new];
            [ add OperatorExpression:one :two];
            [self.mArithmeticExpressionStack addObject:add];
            if (expression.length==0) {
                break;
            }
        }else if ([expression hasPrefix:@"-"]){
            one = [self.mArithmeticExpressionStack lastObject];
            [self.mArithmeticExpressionStack removeLastObject];
            expression =  [expression substringFromIndex:1];
            int b =[expression intValue];
            expression = [self getExpression:expression];
            two = [[NumExpression alloc]initWithNum:b];
            ReduceExpression * add= [ReduceExpression new];
            [ add OperatorExpression:one :two];
            [self.mArithmeticExpressionStack addObject:add];
            if (expression.length==0) {
                break;
            }
        }
        else{
            int a=[expression intValue];
            NumExpression * num = [[NumExpression alloc]initWithNum:a];
            [self.mArithmeticExpressionStack addObject:num];
            expression = [self getExpression:expression];
            if (expression.length==0) {
                break;
            }
        }
    }
}


-(NSString *)getExpression:(NSString *)expression{
    int a=[expression intValue];
    NSString * str =[NSString stringWithFormat:@"%d",a];
    return [expression substringFromIndex:str.length];
}

-(int)calculator{
   id<ArithmeticExpression> object= [self.mArithmeticExpressionStack lastObject];
    [self.mArithmeticExpressionStack removeLastObject];
    return [object interptet];
}

#import "OperatorExpression.h"

@interface ReduceExpression : OperatorExpression

@end


#import "ReduceExpression.h"

@implementation ReduceExpression
-(int)interptet{
    return [_mArithmeticExpression1 interptet]-[_mArithmeticExpression2 interptet];
}
@end

测试代码

 Calculator * calculator = [Calculator new];
    [calculator calculator:@"125+175+100-100"];
    int result= [calculator calculator];
    NSLog(@"result %d",result);
    

测试结果

2018-04-10 16:28:46.682503+0800 行为型设计模式-解释器模式[56577:8311175] result 300

这里需要解释下,要看肯定看的晕

1.ReduceExpression 和AdditionExpression 代表 NonterminalExpression
2.NumExpression代表TerminalExpression
3.Calculator 代表Client
4.ArithmeticExpression 协议代表AbstractExpression
5.vc 就代表context

这里的Calculator 相当于翻译机,将context给的内容翻译成ArithmeticExpression 协议类型的对象保存起来。

例如 "+" 号 ,我们知道是两目操作符,需要两个NumExpression 的数字。在Calculator 中如果遇到+ 号操作符,我们就需要获取两个
NumExpression 类型的数据。获取非常的繁琐。

当时想实现乘法和除法的算法,想想还是算了。乘法和除法运算符需要考虑优先级问题了。就没有实现。实现思路如下图

乘法

demo中还实现了下面的场景

//行为型模式:解释器模式
//场景:开发一套机器人控制程序
/*说明:
    机器人控制程序中包含一些简单的英文控制指令,每一个指令对应一个表达式(expression),
 该表达式可以是简单表达式也可以是复合表达式,每一个简单表达式由移动方向(direction),
 移动方式(action)和移动距离(distance)三部分组成,其中移动方向包括上(up)、下(down)、
 左(left)、右(right);移动方式包括移动(move)和快速移动(run);移动距离为一个正整数。
 两个表达式之间可以通过与(and)连接,形成复合(composite)表达式。
    用户通过对图形化的设置界面进行操作可以创建一个机器人控制指令,机器人在收到指令
后将按照指令的设置进行移动,例如输入控制指令:up move 5,则“向上移动5个单位”;输入控
制指令:down  run 10 and left move 20,则“向下快速移动10个单位再向左移动20个单位”。
*/

/*文法规则
    expression ::= direction action distance | composite //表达式
    composite ::= expression 'and' expression //复合表达式
    direction ::= 'up' | 'down' | 'left' | 'right' //移动方向
    action ::= 'move' | 'run' //移动方式
    distance ::= an integer //移动距离
上述语言一共定义了五条文法规则,对应五个语言单位,这些语言单位可以分为两类,
终结符(也称为终结符表达式):例如direction、action和distance,它们是语言的最小组成单位,不能再进行拆分;
非终结符(也称为非终结符表达式),例如expression和composite,它们都是一个完整的句子,包含一系列终结符或非终结符。
*/


优缺点

优点

1.易于实现文法:在解释器模式中,一条语法规则用一个解释器对象来解释执行。对于解释器的实现来讲,功能就变得比较简单,只需要考虑这一条语法规则的实现就可以了,其他的都不用管。

2.易于扩展新的语法。由于解释器采用类来描述语法规则,因此可以通过继承等机制创建相应的解释器对象,在创建抽象语法树的时候使用这个新的解释器对象就可以了。

缺点

1.执行效率较低。由于在解释器模式中使用了大量的循环和递归调用,因此在解释较为复杂的句子时其速度很慢,而且代码的调试过程也比较麻烦。

2.对于复杂文法难以维护。在解释器模式中,每一条规则至少需要定义一个类,因此如果一个语言包含太多文法规则,类的个数将会急剧增加,导致系统难以管理和维护,此时可以考虑使用语法分析程序等方式来取代解释器模式。


  1. 相关模式

(1)解释器和组合模式

这两种可以组合使用,一般非终结符解释器相当于组合模式中的组合对象,终结符解释器相当于叶子对象。

(2)解释器模式和迭代器模式

由于解释器模式通常使用组合模式来实现,因此在遍历整个对象结构时,可以使用迭代器模式。

(3)解释器模式和享元模式

在使用解释器模式的时候,可能会造成多个细粒度对象,如各式各样的终结符解释器,而这些终结符解释器对不同的表达式来说是一样的,是可以共用的,因此可以引入享元模式来共享这些对象。

(4)解释器模式和访问者模式

在解释器模式中,语法规则和解释器对象是有对应关系的。语法规则的变动意味着功能的变化。自然会导致使用不同的解释器对象;而且一个语法规由可以被不同的解释器解释执行。因此在构建抽象语法树的时候,如果每个节点所对应的解释器对象是固定的,这意味着该节点对应的功能是固定的,那么就不得不根据需要来构建不同的抽象语法树。
为了让构建的抽象语法树较为通用,那就要求解释器的功能不要那么固定,要能很方便地改变解释器的功能,这个时候就变成了如何能够很方便地更改树形结构中节点对象的功能了,访问者模式可以很好的实现这个功能。

[借鉴博客](https://www.cnblogs.com/5iedu/p/5595153.html
源代码地址
下一篇博客
行为型设计模式-迭代器模式

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

推荐阅读更多精彩内容