访问者模式的使用

一、模式介绍

访问者模式通常包含如下几个角色:

  • 抽象元素,定义元素接受访问者访问的方法;
  • 具体元素,提供接受访问者访问的具体实现,以及自身的一些操作;
  • 抽象访问者,定义多个visit方法用来访问每一个具体元素,理论上visit方法个数等于具体元素个数;因此要求该模式使用场景是元素稳定不变的。
  • 具体访问者,实现对具体元素操作的访问;
  • 结构对象,维护具体元素的集合,提供方法接受具体访问者对集合中的所有元素进行访问;

通用的实现代码如下:

/**
 * 抽象元素
 * 定义接受访问者访问的方法
 * 所有具体元素都支持被访问者访问
 */
public interface IElement {
    void accept(IVisitor visitor);
}
/**
 * 具体元素A
 */
@Slf4j
public class ConcreteElementA implements IElement{
    @Override
    public void accept(IVisitor visitor) {
        // 访问者进行访问
        visitor.visit(this);
    }

    public void operation(){
        log.info("ConcreteElementA operation");
    }
}
/**
 * 具体元素B
 */
@Slf4j
public class ConcreteElementB implements IElement{
    @Override
    public void accept(IVisitor visitor) {
        // 访问者进行访问
        visitor.visit(this);
    }

    public void operation(){
        log.info("ConcreteElementB operation");
    }
}
/**
 * 抽象访问者
 */
public interface IVisitor {
    /**
     * 定义visit方法访问每一个具体的元素
     * 理论上visit方法个数和具体元素的个数是相等的
     * @param element
     */
    void visit(ConcreteElementA element);
    void visit(ConcreteElementB element);
}
/**
 * 具体访问者,实现对具体元素的访问操作
 */
public class ConcreteVisitor implements IVisitor{
    @Override
    public void visit(ConcreteElementA element) {
        element.operation();
    }

    @Override
    public void visit(ConcreteElementB element) {
        element.operation();
    }
}
public class ObjectStructure {
    private List<IElement> list = new ArrayList<>();

    /**
     * 需要被访问的具体元素初始化时都装入集合中
     */
    {
        this.list.add(new ConcreteElementA());
        this.list.add(new ConcreteElementB());
    }

    /**
     * 接待某个具体的访问者
     * @param visitor
     */
    public void accept(IVisitor visitor){
        // 带访问者依次参观具体元素
        for (IElement element : this.list) {
            element.accept(visitor);
        }
    }
}
@Slf4j
public class Main {
    public static void main(String[] args) {
        ObjectStructure collection = new ObjectStructure();
        IVisitor jack = new ConcreteVisitor();
        // 接待jack这个访问者
        collection.accept(jack);
    }
}

我们将如上的案例来做一个实际场景的类比:

  • ObjectStructure相当于一个家族的管家,具体元素类相当于每个房间里面的主人,家族里面主人的个数肯定是固定的,然后访问者来家族进行访问的时候,由管家进行接待accept(visitor)
  • 然后管家带领访问者依次去每个房间拜访主人element.accept(visitor)
  • 每个主人需要同意accept(visitor)访问者访问后,访问者才可以进行对当前主人的访问visitor.visit(this)
  • 然后这个主人才和访问者进行交流,展示自己operation

在这样的设计模式中,任何访问者想来拜访,只要让管家去接待一下即可,每个房间的主人们则完全不用操心,只需要固定地接受拜访并展示自己即可,对访问者的扩展十分方便。与此同时,倘若每个房间的主人有多项技能的话(唱歌、跳舞、吟诗、作画......),访问者可以自己定义想拜访主人都需要为自己展示什么才能,只要过了管家这一关即可,因为必须要管家给安排。

二、使用场景

  • JDK的NIO中的FileVisitor接口;
  • Spring中的BeanDefinitionVisitor类;

三、模式总结

3.1 优点

  • 解耦了数据结构和数据操作;
  • 可以很方便地扩展访问者角色,实现对不同数据集的不同操作,扩展性良好;
  • 元素具体类型可以是多样的,访问者均可操作;
  • 各个角色职责分离,符合单一职责原则;

3.2 缺点

  • 无法灵活增加元素类型,否则就得频繁改动访问者,违反了开闭原则;
  • 元素行为增删困难,需要改动访问者的调用行为,违反了开闭原则;
  • 访问者直接耦合了具体元素类,而没有以来抽象,违背了依赖倒置原则;
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容