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