访问者模式:当你想要为一个对象的组合来增加新的能力,并且封装并不重要时,使用组合模式
组合:整体和部分的关系,脱离了整体部分不能存在,比如部门和公司,没有公司就没有部门。
访问模式是行为型模式之一。光看UML似乎并不好理解。所以我对UML图也会给一个具体的说明。

访问者模式类图简介
访问者(Visitor):接口或者抽象类,它定义了对每一个元素(Element)访问的行为,它的参数就是可以访问的元素,它的方法数理论上来讲与元素个数是一样的,因此,访问者模式要求元素的类族要稳定,如果经常添加、移除元素类,必然会导致频繁地修改Visitor接口,否则不适合使用访问者模式。
具体访问者(ConcreteVisitor):具体的访问类,它需要给出对每一个元素类访问时所产生的具体行为。
被访问元素(Element):元素接口或者抽象类,它定义了一个接受访问者的方法(Accept),其意义是指每一个元素都要可以被访问者访问。
具体被访问元素(ConcreteElementA):具体的元素类,它提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法。
结构类(ObjectStructure):定义当中所说的对象结构,对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素供访问者访问。就比如说我们上面提到的公司之于部门中的公司类就是这里的结构类。
举个栗子
一辆汽车有4个轮子,1个引擎。如果说我们要访问汽车,轮子,引擎的具体参数?怎么处理?在每个类中都丢一个getInfomation()?这样显然不便于管理和维护,也不符合基本的设计原则。
访问者(Visitor)
#import <Foundation/Foundation.h>
@class Car;
@class Tire;
@class Engine;
NS_ASSUME_NONNULL_BEGIN
@protocol InfomationVisitor <NSObject>
-(void)visitorCar:(Car *)car;
-(void)visitorTire:(Tire *)tire;
-(void)visitorEngine:(Engine *)engine;
@end
NS_ASSUME_NONNULL_END
被访问元素(Element)
#import <Foundation/Foundation.h>
@protocol InfomationVisitor;
NS_ASSUME_NONNULL_BEGIN
@protocol CarElement <NSObject>
-(void)accept:(id<InfomationVisitor>)visitor;
@end
NS_ASSUME_NONNULL_END
具体访问者(ConcreteVisitor)
#import "CarInfomationVisitor.h"
@implementation CarInfomationVisitor
-(void)visitorCar:(Car *)car {
NSLog(@"%@",car);
}
-(void)visitorEngine:(Engine *)engine {
NSLog(@"%@",engine);
}
-(void)visitorTire:(Tire *)tire {
NSLog(@"%@",tire);
}
@end
Car 结构类(ObjectStructure)
#import "Car.h"
#import "InfomationVisitor.h"
@interface Car ()
@property (nonatomic ,strong, readwrite)NSMutableArray<Tire *> * tires;
@end
@implementation Car
-(NSMutableArray *)tires {
if (!_tires) {
_tires = [NSMutableArray array];
}return _tires;
}
-(void)accept:(id<InfomationVisitor>)visitor {
[visitor visitorCar:self];
[visitor visitorEngine:self.engine];
[self.tires enumerateObjectsUsingBlock:^(Tire * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[visitor visitorTire:obj];
}];
}
-(void)setEngine:(Engine *)engine {
_engine = engine;
}
-(void)addTire:(Tire *)tire {
[self.tires addObject:tire];
}
- (NSString *)description {
return [NSString stringWithFormat:@"<%@:%p,\"%@\">",[self class],self,@{@"name":self.name,@"createTime":self.createTime,@"price":self.price}];
}
@end
Tire 具体被访问元素(ConcreteElementA)
#import "Tire.h"
#import "InfomationVisitor.h"
@implementation Tire
-(void)accept:(id<InfomationVisitor>)visitor {
[visitor visitorTire:self];
}
- (NSString *)description {
return [NSString stringWithFormat:@"<%@:%p,\"%@\">",[self class],self,@{@"color":self.color,@"material":self.material}];
}
@end
Engine 具体被访问元素(ConcreteElementB)
#import "Engine.h"
#import "InfomationVisitor.h"
@implementation Engine
-(void)accept:(id<InfomationVisitor>)visitor {
[visitor visitorEngine:self];
}
- (NSString *)description {
return [NSString stringWithFormat:@"<%@:%p,\"%@\">",[self class],self,@{@"cylinderNum":@(self.cylinderNum),@"power":self.power}];
}
@end
具体调用
#import <Foundation/Foundation.h>
#import "Car.h"
#import "Tire.h"
#import "Engine.h"
#import "CarInfomationVisitor.h"
int main(int argc, const char * argv[]) {
Car * car = [[Car alloc] init];
car.name = @"奔驰";
car.createTime = @"2018-12-10";
car.price = @"100w";
NSArray * colors = @[@"红色",@"橙色",@"黄色",@"绿色"];
for (int i = 0; i < 4; i++) {
Tire * tire = [[Tire alloc] init];
tire.color = colors[i];
tire.material = @"橡胶";
[car addTire:tire];
}
Engine *engin = [[Engine alloc] init];
engin.cylinderNum = 4;
engin.power = @"110kw";
car.engine = engin;
CarInfomationVisitor * visitor = [[CarInfomationVisitor alloc] init];
[car accept:visitor];
return 0;
}
优点
1.允许组合加入新的操作而不影响结构本身
2.想要加入新的操作相对容易
3.访问者加入的操作是集中在一起的
缺点
1.采用访问者会打破组合的封装
2.采用访问者模式,会导致组合结构的改变变得困难。
网友评论