访问者模式的定义
定义: 封装一些作用于某种数据结构中的各元素的操作, 它可以在不改变数据结构的前提下定义作用于这些元素的新的操作
通俗的说, 就是定义一个访问者角色, 当对指定角色进行访问时要通过访问者进行访问
其类图如下:
各角色说明:
- Vistor 抽象访问者: 抽象类或接口, 声明访问者可以访问哪些元素, 具体到程序中就是 visit 方法的参数定义哪些对象是可以被访问的
- ConcreteVistor 具体访问者: 它影响访问者访问到一个类后该怎么干, 要做什么事
- Element 抽象元素: 接口或抽象类, 声明接受哪一类访问者访问, 程序上是通过 accept 方法中的参数来定义的
- ConcreteElement 具体元素: 实现 accept方法, 通常是 visitor.visit(this); 基本上都形成一种模式了
- ObjectStruture 结构对象: 元素的生成者, 一般容纳在多个不同类、不同接口的容器, 项目中一般很少抽象出这个角色
抽象元素代码:
具体元素代码:
抽象访问者代码:
具体访问者代码:
结构对象用来产生不同的元素对象, 代码如下:
场景类代码:
通过增加访问者, 这要是具体元素就非常容易访问, 对元素的遍历就更加容易了, 甭管它是什么对象, 只要它在一个容器中, 都可以通过访问者来访问, 任务集中化.
访问者模式的应用
访问者模式的优点:
- 符合单一职责原则. 具体元素角色负责数据的加载, 而访问者角色负责报表的展现, 两个不同的职责非常明确的分离开来, 各自演绎变化
- 优秀的扩展. 由于职责分开,继续增加 对数据的操作是非常快捷的.
- 灵活性非常高. 例如, 当需要对不同的具体元素进行分别统计时, 使用 instanceof 循环判断当然也可以, 但是现在有一个好办法, 那就是把它丢给访问者,由访问者来进行统计计算
访问者模式的缺点:
- 具体元素对访问者公布细节. 访问者要访问一个类就必然要求这个类公布一些方法和数据, 也就是说访问者关注了其他类的内部细节, 这也是迪米特法则所不建议的
- 具体元素变更比较困难. 具体元素角色的增加、删除、修改是比较困难的
- 违背了依赖倒置原则. 访问者依赖的是具体元素, 而不是抽象元素, 这破坏了依赖倒置原则, 特别是在面向对象的编程中, 抛弃了对 接口的依赖,而直接依赖实现类, 扩展比较难
访问者模式的应用场景:
- 一个对象结构包含很多类对象, 它们有不同的接口, 而你想对这些对象实施一些依赖于其具体类的操作, 也就是说用迭代器模式已经不能胜任的情景
- 需要对一个对象结构中的对象进行很多不同并且不相关的操作, 而你想避免让这些操作"污染"这些对象的类
- 业务规则要求遍历多个不同的对象. 这本身也是访问者模式的出发点, 访问者模式是对迭代器模式的扩充, 可以便利不同的对象, 然后执行不同的操作, 也就是针对访问的对象不同,执行不同的操作.
- 访问者模式还有一个 用途, 充当拦截器角色
访问者模式的扩展
1.统计功能
对不同的具体元素进行统计, 针对不同的具体元素针对性统计
比如统计员工工资, 经理和员工的工资情况不同, 使用访问者进行分别计算, 然后可以计算总额等
2.多个访问者
可以定义多个访问者, 每个访问者实现不同的功能
在IVistor 下再定义 接口继承 IVistor, 分别实现各自功能, 使用时传递不同的访问者有不同的表现
访问者模式是一种集中规整模式,特别使用于大规模重构的项目, 在这一个阶段需求已经非常清晰, 原系统的功能点也已经明确, 通过访问者模式可以很容易把一些功能进行梳理, 达到最终目的--功能集中化, 如一个统一的报表运算、UI展现等, 我们还可以与其它模式混编建立一套自己的过滤器或者拦截器