介绍
访问者模式的基本思想是,软件系统中拥有一个由许多对象构成的,比较稳定的对象结构,这些对象的类都拥有一个 accept 方法用来接收访问者对象的访问(这个方法需要的参数为访问者类,调用访问者类的方法完成访问),访问者是一个接口,它拥有一个 visit 方法,这个方法对访问到的对象结构中不同类型的元素作不同的处理。在对象结构的一次访问过程中,我们遍历整个对象结构(这里的整个对象结构可以理解为类似集合,就是说这些被访问的对象都可以通过遍历得到),对每一个元素都实施 accept 方法,在每一个元素的 accept 方法中又会调用访问者的 visit 方法,从而使访问者可以处理对象结构的每一个元素,我们可以针对对象结构设计不同的访问者类来完成不同的操作,达到区别对待的效果。
比较抽象不好理解,这里可以理解为,公司有五种员工对应上面的对象结构也就是被访问者,还有两种管理层对应上面的访问者,两种管理层都可以管理五种不同的员工,但是,五种员工被两种管理层管理的方式不一样。
这时候可以我们定义的五种员工都有一个 accept 方法,可以将管理层对象传入,两个管理层对象中也分别定义了自己对五种员工不同管理的方法,这样在员工的 accept 方法中,调用相应管理者的管理自己对应的方法。这样在遍历管理五种员工时,就可以通过传入不同的管理层达到不同的管理效果。
定义
封装一些作用于某些数据结构中各元素的操作,它可以在不改变这个数据结构的前提下定义作用于这些元素的新的操作。
使用场景
对象结构比较稳定,但经常需要在此对象结构上定义新的操作 比如上例中,五种员工比较稳定不会变,又来了董事长可以管理这五种员工,但是他的管理跟上面两个管理层的还不一样,这时候只需要定义一个董事长并实现对五种员工管理的 visit 方法即可
需要对一个对象结构中的对象进行很多不同且不相关的操作,而需要避免这些操作 "污染这些类",也不希望在增加新操作时修改这些类。 还是以上例来说,如果我们在五种员工中定义不同管理者访问他们的方法,那么每次需要新加管理者时都需要在五种员工类中增加方法,或者某种管理层的管理方法有变化时这五个类都得改,太不面向对象了,所以把管理层抽象出来,增加管理层时或者管理层逻辑改变时都不影响五种员工类。
角色介绍
Visitor 接口或者抽象类,它定义了对每一个元素 (Element) 访问的行为,它的参数就是可以访问的元素,它的方法个数理论上来讲与元素个数是一样的,因此,访问者模式要求元素的类族要稳定(上例中,五种员工类别不能改),如果经常添加、移除元素类,必然会导致频繁地修改 Visitor 接口,如果出现这种情况,说明不适合使用访问者模式。
ConcreteVisitor 具体的访问者,它需要给出每一个元素类访问时所产生的具体行为
Element 元素接口或抽象类,它定义了一个接受访问者(accept)的方法,其意义是值每一个元素都要可以被访问者访问。
ElementA/ElementB 具体的元素类,它提供接受访问方法的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法
ObjectStructure 定义当中所提到的对象结构,对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素访问者访问。
Android 源码中的访问者模式 - 编译期注解
编译注解核心原理依赖 APT(Annotation Processing Tools),编译期注解的工作过程就是使用了访问者模式,具体编译期注解的工作过程,会写一篇新的文章来分析。
优点
个角色职责分离,符合单一职责原则
具有优秀的扩展性
使得数据结构和作用于结构上的操作解耦,使得操作集合可以独立变化
灵活性
缺点
- 具体元素对访问者公布细节,违反了迪米勒原则
- 具体元素变时导致修改成本大
- 违反了依赖导致原则,为了达到 “区别对待” 而依赖了具体类,没有依赖对象。