一、定义
封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。
二、类图及角色
1. Visitor 抽象访问者
抽象类或者接口,声明访问者可以访问哪些元素,具体到程序中就是 visit
方法的参数定义哪些对象是可以被访问的。
public interface IVisitor {
// 可以访问哪些对象
public void visit(ConcreteElement1 el1);
public void visit(ConcreteElement2 el2);
}
2. ConcreteVisitor 具体访问者
Visitor 的实现类,定义一个访问者访问到对象后需要做哪些事情。
public class Visitor implements IVisitor {
// 访问el1元素
public void visit(ConcreteElement1 el1) {
el1.doSomething();
}
// 访问el2元素
public void visit(ConcreteElement2 el2) {
el2.doSomething();
}
}
3. Element 抽象元素
接口或者抽象类,声明接受哪一类访问者访问,程序上是通过 accept
的参数来定义的。
public abstract class Element {
// 定义业务逻辑
public abstract void doSomething();
// 允许谁来访问
public abstract void accept(IVisitor visitor);
}
4. ConcreteElement 具体元素
实现 accept
方法,通常是 visitor.visit(this)
,基本上已经是默认的写法了。
public class ConcreteElement1 extends Element{
// 完善业务逻辑
public void doSomething(){
// 业务处理
}
// 允许那个访问者访问
public void accept(IVisitor visitor){
visitor.visit(this);
}
}
public class ConcreteElement2 extends Element{
// 完善业务逻辑
public void doSomething(){
// 业务处理
}
// 允许那个访问者访问
public void accept(IVisitor visitor){
visitor.visit(this);
}
}
5. ObjectStructure 结构对象
提供一个高层接口以供访问者访问其中的元素,一般很少抽象出去,通常是一个集合,例如 List、Set、Map 等。
public class ObjectStruture {
//对象生成器,这里通过一个工厂方法模式模拟
public static Element createElement() {
Random rand = new Random();
if(rand.nextInt(100) > 50){
return new ConcreteElement1();
} else {
return new ConcreteElement2();
}
}
}
6. 模拟一个场景
public class Client {
public static void main(String[] args) {
for(int i=0;i<10;i++){
//获得元素对象
Element e = ObjectStruture.createElement();
//接受访问者访问
e.accept(new Visitor());
}
}
}
三、优缺点
- 优点
- 符合单一职责原则
访问者和被访问者独立分开,各自负责各自的角色和职责。 - 优秀的扩展性
元素类可以通过接受不同的访问者就可以实现不同操作的扩展。
- 缺点
- 元素类对访问者是公布细节的
访问者要访问一个类必然要求这个类公布一些方法和属性,这一点是迪米特法则(最少知道法则)不建议的。 - 违背了依赖倒置原则
访问者依赖的都是具体元素,而不是抽象元素,抛弃了对接口的依赖,而是直接依赖实现类,扩展比较难。
四、使用场景
- 一个对象结构包含很多类对象,它们有不同的接口,而你想对这些对象实施一些依赖于其具体类的操作。
- 需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。访问者模式使得你可以将相关的操作集中起来定义在一个类中。
- 当该对象结构被很多应用共享时,用访问者模式让每个应用仅包含需要用到的操作。
- 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。
查看更多:设计模式分类以及六大设计原则