行为型设计模式-访问者模式
定义
主要将数据结构与数据操作分离。
解决问题
稳定的数据结构和易变的操作耦合问题。
使用场景
需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,使用访问者模式将这些封装到类中。
角色
![](https://img.haomeiwen.com/i1682758/1dd0744b69534c77.png)
Visitor:接口或者抽象类,它定义了对每一个元素(Element)访问的行为,它的参数就是可以访问的元素,它的方法数理论上来讲与元素个数是一样的,因此,访问者模式要求元素的类族要稳定,如果经常添加、移除元素类,必然会导致频繁地修改Visitor接口,如果这样则不适合使用访问者模式。
ConcreteVisitor:具体的访问类,它需要给出对每一个元素类访问时所产生的具体行为。
Element:元素接口或者抽象类,它定义了一个接受访问者的方法(Accept),其意义是指每一个元素都要可以被访问者访问。
ConcreteElement:具体的元素类,它提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。
ObjectStructure:定义当中所说的对象结构,对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素供访问者访问。
场景模拟
老板或者会计查看账本的收支情况
访问者模式UML图
![](https://img.haomeiwen.com/i1682758/b715bcfdde443755.png)
代码
#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 就相当于看了一条账单。
造成这种问题的原因就是因为你中有我我中有你。
![](https://img.haomeiwen.com/i1682758/13d5151ee0f4826e.png)
正常情况是你中有我我中有你,互相持有。
![](https://img.haomeiwen.com/i1682758/537a3b2a107c2e40.png)
这种结果耦合性太高,怎么办?引入了第三者Account,打破一条连接,这样容易扩展。
23 种设计模式到此学习完毕。
由于没有系统的学过UML图,UML 图做的有点烂。为了以后写博客画图正规化,接下来开始学习UML