访问者模式(Visitor Pattern)

Posted 顧棟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了访问者模式(Visitor Pattern)相关的知识,希望对你有一定的参考价值。

访问者模式(Visitor Pattern)

访问者模式的定义

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

访问者模式的优点

  1. 扩展性好。由于职责分开,能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
  2. 复用性好。可以通过访问者来定义整个对象结构通用的功能,从而提高系统的复用程度。
  3. 灵活性好。访问者模式将数据结构与作用于结构上的操作解耦,使得操作集合可相对自由地演化而不影响系统的数据结构。
  4. 符合单一职责原则。访问者模式把相关的行为封装在一起,构成一个访问者,使每一个访问者的功能都比较单一。

访问者模式的缺点

  1. 增加新的元素类很困难。在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。
  2. 违反了依赖倒置原则。访问者模式依赖了具体类,而没有依赖抽象类。
  3. 具体元素对访问者公布细节,访问者要访问一个类就必然要求这个类公布一些方法和数据,也就是说访问者关注了其
    他类的内部细节,这破坏了对象的封装性。这也是是迪米特法则所不建议的。

访问者模式的结构

访问者模式包含以下主要角色。

  1. 抽象访问者(Visitor)角色:抽象类或者接口,为每个具体元素类对应一个访问操作 visit() ,声明访问者可以访问哪些元素,具体到程序中就是visit方法的参数定义哪些对象是可以被访问的。该操作中的参数类型标识了被访问的具体元素。
  2. 具体访问者(ConcreteVisitor)角色:实现抽象访问者角色中声明的各个访问操作,它影响访问者访问到一个类后该怎么干,要做什么事情。
  3. 抽象元素(Element)角色:接口或者抽象类,声明接受哪一类访问者访问,程序上是通过accept方法中的参数来定义的。
  4. 具体元素(ConcreteElement)角色:实现accept方法,通常是visitor.visit(this),基本上都形成了一种模式了,另外具体元素中可能还包含本身业务逻辑的相关操作。
  5. 对象结构(Object Structure)角色:元素产生者,一般容纳在多个不同类、不同接口的容器,如List、Set、Map等,在项目
    中,一般很少抽象出这个角色。

访问者模式的实现

一般实现

/**
 * 抽象元素
 */
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("ConcreteElement1 is doing something");
    }

    /**
     * 允许谁来访问
     */
    @Override
    public void accept(IVisitor visitor) {
        visitor.visit(this);
    }
}
/**
 * 具体元素2
 */
public class ConcreteElement2 extends Element {

    /**
     * 定义业务逻辑
     */
    @Override
    public void doSomething() {
        System.out.println("ConcreteElement2 is doing something");
    }

    /**
     * 允许谁来访问
     */
    @Override
    public void accept(IVisitor visitor) {
        visitor.visit(this);
    }
}
/**
 * 抽象访问者
 */
public interface IVisitor {
    /**
     * 访问具体元素1
     */
    public void visit(ConcreteElement1 el1);

    /**
     * 访问具体元素2
     */
    public void visit(ConcreteElement2 el2);
}
/**
 * 具体访问者
 */
public class Visitor implements IVisitor {
    @Override
    public void visit(ConcreteElement1 el1) {
        el1.doSomething();
    }

    @Override
    public void visit(ConcreteElement2 el2) {
        el2.doSomething();
    }
}
/**
 * 结构对象
 */
public class ObjectStructure {

    private final List<Element> list = new ArrayList<>();

    public void accept(Visitor visitor) {
        for (Element element : list) {
            element.accept(visitor);
        }
    }

    public void add(Element element) {
        list.add(element);
    }

    public void remove(Element element) {
        list.remove(element);
    }
}
    public static void main(String[] args) {
        ObjectStructure objectStructure = new ObjectStructure();

        //获得元素对象
        Element el1 = new ConcreteElement1();
        Element el2 = new ConcreteElement2();
        objectStructure.add(el1);
        objectStructure.add(el2);

        for (int i = 0; i < 5; i++) {
            //接受访问者访问
            objectStructure.accept(new Visitor());
        }
    }

适用场景

  1. 对象结构相对稳定,但其操作算法经常变化的程序。

  2. 对象结构中的对象需要提供 多种不同 且 不相关的操作,而且要避免让这些操作的变化影响对象的结构。

  3. 对象结构包含很多类型的对象,希望对这些对象实施一些依赖于其具体类型的操作。


本文主要参考:

  1. 小傅哥的《重学Java模式》
  2. 《C语言中文网》设计模式的相关内容
  3. 《设计模式之禅》第二版 秦小波

以上是关于访问者模式(Visitor Pattern)的主要内容,如果未能解决你的问题,请参考以下文章

访问者模式(Visitor Pattern)

访问者模式(Visitor Pattern)

22访问者模式(Visitor Pattern)

秒懂设计模式之访问者模式(Visitor Pattern)

尚硅谷设计模式学习(16)---[访问者模式(Visitor Pattern)]

二十三种设计模式[23] - 访问者模式(Visitor Pattern)