行为型设计模式-访问者模式

定义

主要将数据结构与数据操作分离。


解决问题

稳定的数据结构和易变的操作耦合问题。


使用场景

需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,使用访问者模式将这些封装到类中。


角色

image.png

Visitor:接口或者抽象类,它定义了对每一个元素(Element)访问的行为,它的参数就是可以访问的元素,它的方法数理论上来讲与元素个数是一样的,因此,访问者模式要求元素的类族要稳定,如果经常添加、移除元素类,必然会导致频繁地修改Visitor接口,如果这样则不适合使用访问者模式。

ConcreteVisitor:具体的访问类,它需要给出对每一个元素类访问时所产生的具体行为。

Element:元素接口或者抽象类,它定义了一个接受访问者的方法(Accept),其意义是指每一个元素都要可以被访问者访问。

ConcreteElement:具体的元素类,它提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。

ObjectStructure:定义当中所说的对象结构,对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素供访问者访问。


场景模拟

老板或者会计查看账本的收支情况

访问者模式UML图

访问者模式UML图

代码

#import <Foundation/Foundation.h>
#import "AccountBookViewer.h"
@protocol Bill <NSObject>

-(void)accept:(id<AccountBookViewer>)viewer;

@end

#import <Foundation/Foundation.h>

@class IncomeBill,ConsumeBill;
@protocol AccountBookViewer <NSObject>
-(void)lookIncomBill:(IncomeBill*)incomeBill;
-(void)lookConsumeBill:(ConsumeBill *)consumeBill;
@end

#import <Foundation/Foundation.h>
#import "Bill.h"
@interface ConsumeBill : NSObject<Bill>
@property (nonatomic,assign) double amount;
@property (nonatomic,strong) NSString *item;
- (instancetype)initWithItem:(NSString *)item Amount:(double)amount;
@end

#import "ConsumeBill.h"


@implementation ConsumeBill

- (instancetype)initWithItem:(NSString *)item Amount:(double)amount
{
    self = [super init];
    if (self) {
        self.item = item;
        self.amount =amount;
    }
    return self;
}

-(void)accept:(id<AccountBookViewer>)viewer{
    [viewer lookConsumeBill:self];
}
@end

#import <Foundation/Foundation.h>
#import "Bill.h"
@interface IncomeBill : NSObject<Bill>
@property (nonatomic,assign) double amount;
@property (nonatomic,strong) NSString *item;
- (instancetype)initWithItem:(NSString *)item Amount:(double)amount;
@end
#import "IncomeBill.h"

@implementation IncomeBill
- (instancetype)initWithItem:(NSString *)item Amount:(double)amount
{
    self = [super init];
    if (self) {
        self.item = item;
        self.amount =amount;
    }
    return self;
}
-(void)accept:(id<AccountBookViewer>)viewer{
    [viewer lookIncomBill:self];
}
@end
#import <Foundation/Foundation.h>
#import "AccountBookViewer.h"
@interface Boss : NSObject<AccountBookViewer>
@property (nonatomic,assign) double totalIncome;
@property (nonatomic,assign) double  totalConsume;
@end


#import "Boss.h"
#import "IncomeBill.h"
#import "ConsumeBill.h"

@implementation Boss
-(void)lookIncomBill:(IncomeBill *)incomeBill{
    self.totalIncome += incomeBill.amount;
}

-(void)lookConsumeBill:(ConsumeBill *)consumeBill{
    self.totalConsume = consumeBill.amount;
    
}

@end
#import <Foundation/Foundation.h>
#import "AccountBookViewer.h"
@interface CAP : NSObject<AccountBookViewer>

@end

#import "CAP.h"
#import "IncomeBill.h"
#import "ConsumeBill.h"
@implementation CAP
-(void)lookIncomBill:(IncomeBill *)incomeBill{
    NSLog(@"是否交税了");
}

-(void)lookConsumeBill:(ConsumeBill *)consumeBill{
    if ([consumeBill.item isEqualToString:@"工资"]) {
        NSLog(@"是否交个人所得税");
    }
}
@end

#import <Foundation/Foundation.h>
#import "Bill.h"
#import "AccountBookViewer.h"
@interface AccountBook : NSObject
-(void)addBill:(id<Bill>)bill;
-(void)show:(id<AccountBookViewer>)view;
@end
#import "AccountBook.h"
@interface AccountBook()
@property (nonatomic,strong) NSMutableArray *billList;
@end
@implementation AccountBook

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.billList = [NSMutableArray new];
    }
    return self;
}
-(void)addBill:(id<Bill>)bill{
    [self.billList addObject:bill];
}
-(void)show:(id<AccountBookViewer>)view{
    for (id<Bill>bill in self.billList) {
        [bill accept:view];
    }
}
@end

测试代码

 AccountBook * accountBook= [AccountBook new];
    IncomeBill * bill = [IncomeBill new];
    bill.item=@"卖广告";
    bill.amount = 10000;
    [accountBook addBill:bill];
    bill = [IncomeBill new];
    bill.item=@"卖商品";
    bill.amount = 20000;
    [accountBook addBill:bill];

    ConsumeBill *consume = [ConsumeBill new];
    consume.item = @"工资";
    consume.amount = 1000;
    [accountBook addBill:consume];

    consume = [ConsumeBill new];
    consume.item = @"材料费";
    consume.amount = 2000;
    [accountBook addBill:consume];
    
    
    Boss * boss = [Boss new];
    [accountBook show:boss];
    CAP * cap = [CAP new];
    [accountBook show:cap];
    
    NSLog(@"收入:%lf 支出: %lf",boss.totalIncome,boss.totalConsume);
    

测试结果

2018-04-11 17:19:21.712291+0800 行为型设计模式-访问者模式[74169:8838021] 是否交税了
2018-04-11 17:19:21.712484+0800 行为型设计模式-访问者模式[74169:8838021] 是否交税了
2018-04-11 17:19:21.712583+0800 行为型设计模式-访问者模式[74169:8838021] 是否交个人所得税
2018-04-11 17:19:21.712682+0800 行为型设计模式-访问者模式[74169:8838021] 收入:30000.000000 支出: 2000.000000

优缺点

优点

1、符合单一职责原则。
2、优秀的扩展性。
3、灵活性。

缺点

1、具体元素对访问者公布细节,违反了迪米特原则。
2、具体元素变更比较困难。
3、违反了依赖倒置原则,依赖了具体类,没有依赖抽象。


总结

学完这个模式,感觉这个模式还是有点复杂。总结下

1.从例子看出来,这里面的关键类是AccountBook,他是桥梁
2.AccountBook 类,管理所有的账单。
3.AccountBook类 ,提供一个方法供view 检查。在这里,view 和账单发生了关系。
4.ACcountBook 账单发生检查的时候,是具体对象,会分配到具体的类中执行。
5.在每个具体的Bill中,会调用AccountBookViewer这规定的方法。
这样AccountBookViewer 就相当于看了一条账单。

造成这种问题的原因就是因为你中有我我中有你。

正常情况

正常情况是你中有我我中有你,互相持有。

image.png

这种结果耦合性太高,怎么办?引入了第三者Account,打破一条连接,这样容易扩展。


源代码地址

23 种设计模式到此学习完毕。
由于没有系统的学过UML图,UML 图做的有点烂。为了以后写博客画图正规化,接下来开始学习UML

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

推荐阅读更多精彩内容