Skip to content

访问者模式

定义

封装一些作用于某种数据结构中的各元素的操作,它可以在不改变数据结构的前提下定义作用于这些元素的新的操作。

通用源码结构

IVisitor        // 访问者接口
Visitor         // 具体访问者
Element         // 抽象元素类
ConcreteElement1// 具体元素类1
ConcreteElement2// 具体元素类2
ObjectStructure // 结构对象 - 暂时用于产生具体元素

元素抽象类和具体元素类

public abstract class Element {

    public abstract void doSomething();

    public abstract void accept(IVisitor visitor); // 依赖访问者接口

}

// 具体元素类1
public class ConcreteElement1 extends Element {
    @Override
    public void doSomething() {
        // 业务逻辑放这里
        System.out.println(getClass().getSimpleName() + " do something.");
    }

    @Override
    public void accept(IVisitor visitor) {
        visitor.visit(this); // 将自身传入来访者
    }
}
public class ConcreteElement2 extends Element {
    @Override
    public void doSomething() {
        System.out.println(getClass().getSimpleName() + " do something.");
    }

    @Override
    public void accept(IVisitor visitor) {
        visitor.visit(this); // 将自身传入来访者
    }
}

访问者接口,依赖了具体的元素类

public interface IVisitor {

    void visit(ConcreteElement1 element1);

    void visit(ConcreteElement2 element2);
}

public class Visitor implements IVisitor {

    @Override
    public void visit(ConcreteElement1 element1) {
        element1.doSomething(); // 实现具体的访问操作
    }

    @Override
    public void visit(ConcreteElement2 element2) {
        element2.doSomething();
    }
}

结构对象 - 暂时用于产生具体元素

// 生成具体的元素类
public class ObjectStructure {
    public static Element createElement() {
        Random random = new Random();// 随机产生一些具体元素类实例
        if (random.nextInt(100) > 50) {
            return new ConcreteElement1();
        } else {
            return new ConcreteElement2();
        }
    }
}

测试代码

Visitor visitor = new Visitor();
for (int i = 0; i < 4; i++) {
    Element el = ObjectStructure.createElement();
    el.accept(visitor);
}

/*
 output
 ConcreteElement1 do something.
 ConcreteElement2 do something.
 ConcreteElement2 do something.
 ConcreteElement2 do something.
 */

访问者模式的应用

优点

  • 符合单一职责原则
    具体元素角色负责数据的加载,而访问者负责数据的展现。
  • 扩展性好,灵活性高
    职责分开,访问者可以灵活的扩展方法

缺点

具体元素对访问者公布细节
访问者要访问一个类就必然要求这个类公布一些方法和数据,也就是访问者关注了其他类的内部 细节,这是迪米特法则不建议的

具体元素变更比较困难
如果具体元素增加属性,对应的所有visitor也要修改

违背了依赖倒置原则
访问者依赖的是具体元素而不是抽象元素,破坏了依赖倒置原则。

使用场景

一个对象结构包含很多类对象,它们有不同的接口,而需要对这些对象实施一些依赖其具体类的操作,也就是迭代器模式已不能胜任的场景。

需要对一个对象结构中的对象进行很多不同并且不相关的操作。

总结:业务规则要求遍历多个不同的对象。

参考:《设计模式之禅》 秦小波