访问者模式
1、什么是访问者模式
表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作。
访问者模式主要由三个角色组成
- 访问者
- 访问元素
- 元素集合(可以不是对象)
访问者就是对访问元素进行操作的访问对象。有可以对访问元素操作的方法。
访问元素即可以执行动作的对象。是可以被访问者修改的对象。
元素集合则顾名思义是访问元素的集合,将访问元素放到元素集合当中,等待访问。
以游戏而言,有许多种建筑物,居民楼、教堂、兵营等,这些都是访问元素,而访问者有清洁工、厨师等。整个游戏就是元素集合。
清洁工可以提升三者的清洁度,并且会提高居民楼的舒适度,教堂的信仰度,兵营的训练热情度等(想想看脏乱不堪的和干净整洁的区别);
厨师可以提升三者的幸福感(有好吃的总会让人感到幸福),并且会提高教堂的人气值,兵营的士气值;
这时候增加了一种访问者:客人
客人可以提升居民楼和教堂的人气值,教堂的传播度,兵营不允许访问。
这个时候通过有两种选择:
- 在居民楼、教堂、兵营等所有类中实现访问者能实现的事情,当增加访问者的时候则所有的访问元素类都需要进行修改
- 将访问者抽象出来,实现访问者模式,当增加访问者的时候,增加一个访问者类并实现对应方法即可
2、访问者模式用在什么地方
- 一个对象结构(元素集合)包含很多类对象(不同的访问元素对象),它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
- 当你需要对一个对象结构中的对象进行很多不同的并且不相关的操作(可以直接在外界调用,属于可有可无的功能),并且不像因为这些操作而让对象变得过分拥挤。
Visitor 使得你可以将相关的操作集中起来定义在一个类中。当该对象结构被很多应用共享时,用 Visitor 模式让每个应用仅包含需要用到的操作。 - 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。
3、访问者模式的使用
- 定义访问元素,包括抽象对象与具体对象。
- 定义访问者,在抽象对象中访问者定义执行动作,访问元素中接收访问者的方法。
访问元素增加 acceptVisitor(visitor) 方法(接收访问者),访问者增加 visitA(A)、visitB(B)、visitC(C) 方法(根据元素对象的多少) - 通过访问元素调用访问者中的事件。在访问元素的 acceptVisitor 的实现方法中调用 [visitor visitX:self] 执行方法。
4、总结
访问者模式属于方法的扩展。优点如下:
在不同的对象中,如果存在大量重复的代码,可以把代码封装到访问者中。降低代码的冗余度。
易于增加访问者,通过访问者实现不同的方法。在不更改访问元素结构的前提下增加实现的方法。
不过同时也有一个很严重的缺点:
如果增加了一种访问元素,那么所有的访问者都需要增加对应的方法,导致增加访问元素的成本增加。
所以,最好在能确定具体元素数量的时候再使用访问者模式。
5、demo
class Visitor: NSObject {
/// 访问元素A
func visitA(element :VisitElementA) {
}
/// 访问元素B
func visitB(element :VisitElementB) {
}
}
class VisitorA: Visitor {
override func visitA(element: VisitElementA) {
NSLog("No1 Visit1 %@", element)
/// 用 element 做某些操作
}
override func visitB(element: VisitElementB) {
NSLog("No1 Visit2 %@", element)
/// 用 element 做某些操作
}
}
class VisitorB: Visitor {
override func visitA(element: VisitElementA) {
NSLog("No2 Visit1 %@", element)
/// 用 element 做某些操作
}
override func visitB(element: VisitElementB) {
NSLog("No2 Visit2 %@", element)
/// 用 element 做某些操作
}
}
class VisitElement: NSObject {
func acceptVisit(visit :Visitor) {
}
}
class VisitElementA: VisitElement {
override func acceptVisit(visit :Visitor) {
visit.visitA(element: self)
}
}
class VisitElementB: VisitElement {
override func acceptVisit(visit :Visitor) {
visit.visitB(element: self)
}
}
class Client: NSObject {
func begin() {
let visit1 = VisitorA()
let visit2 = VisitorB()
let element1 = VisitElementA()
let element2 = VisitElementA()
let element3 = VisitElementA()
let element4 = VisitElementB()
let element5 = VisitElementB()
let array = [element1,element2,element3,element4,element5]
for element in array {
let number = arc4random()
if number%2 == 0 {
element.acceptVisit(visit: visit1)
}
else {
element.acceptVisit(visit: visit2)
}
}
}
}