深圳幻海软件技术有限公司 欢迎您!

初识设计模式 - 访问者模式

2023-03-13

简介访问者设计模式(VisitorDesignPattern)的定义是,允许一个或多个操作应用到一组对象上,解耦操作和对象本身。在使用访问者模式的时候,被访问的元素通常不是单独存在的,它们存储在一个集合中,这个集合称为“对象结构”,访问者通过遍历对象结构实现对其存储的元素进行逐个访问。访问者模式使用

简介

访问者设计模式(Visitor Design Pattern)的定义是,允许一个或多个操作应用到一组对象上,解耦操作和对象本身。

在使用访问者模式的时候,被访问的元素通常不是单独存在的,它们存储在一个集合中,这个集合称为“对象结构”,访问者通过遍历对象结构实现对其存储的元素进行逐个访问。

访问者模式使用了“双重分派”的调用机制,即元素对象定义一个操作方法支持注入访问者对象,在操作方法内调用访问者的访问方法,并将当前元素对象传入到访问方法中。

具体实现

在这里举一个工作当中的具体例子,在小公司的项目组当中,名义上区分了开发、测试等岗位,但实际上开发人员既要会开发,也有会测试,对于测试人员也是同样的要求,既要会测试,也要会开发。

在这里案例当中,开发人员、测试人员统称为元素,我们在这里先构建一个抽象的元素类。其代码示例如下:

public interface Element {
    // 定义一个接受访问者访问的抽象方法
    void accept(Visitor visitor);
}

对于开发人员类,根据自己的情况实现这个 accept() 方法,其代码如下:

public class Programmer implements Element {
    private String name = "开发人员";

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visitProgrammer(this);
    }
}

对于测试人员,根据自己的情况实现这个 accept() 方法,其代码如下:

public class Tester implements Element {
    private String name = "测试人员";

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visitTester(this);
    }
}

第二步,最重要的就是要定义好一个访问者类,在这里抽象出的访问者接口可以是以项目组为范围,为项目组中的每一个元素定义对应的访问方法。其代码示例如下:

public interface Visitor {
    void visitProgrammer(Programmer programmer);

    void visitTester(Tester tester);
}

每当出现一个新的操作时,就可以实现访问者接口,注入不同的元素对象以实现不同的操作。

如下是开发人员和测试人员使用开发技能的代码示例:

public class DevelopVisitor implements Visitor {
    @Override
    public void visitProgrammer(Programmer programmer) {
        System.out.println(programmer.getName() + "在开发");
    }

    @Override
    public void visitTester(Tester tester) {
        System.out.println(tester.getName() + "在开发");
    }
}

如下是开发人员和测试人员使用测试技能的代码示例:

public class TestVisitor implements Visitor {
    @Override
    public void visitProgrammer(Programmer programmer) {
        System.out.println(programmer.getName() + "在测试");
    }

    @Override
    public void visitTester(Tester tester) {
        System.out.println(tester.getName() + "在测试");
    }
}

总结

优点

访问者模式的主要优点如下:

  • 能够在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能,符合开闭原则
  • 将有关元素的行为都封装到一个访问者对象中,每个访问者对象的功能都比较单一,符合单一职责原则

缺点

访问者模式的主要缺点如下:

  • 增加新的元素类需要在每一个访问者类中都增加相应的具体操作,这违背了开闭原则
  • 访问者对象可以访问并调用每一个元素对象的操作,这意味着元素对象有时候会暴露一些内部操作和内部状态,破坏了封装
  • 访问者模式依赖了具体类,而没有依赖抽象类,违反了依赖倒置原则

适用场景

访问者模式的适用场景如下:

  • 对象结构中元素对象的类很少改变,但经常需要在此对象结构上定义新的操作

源码

访问者模式提供一个方便的可维护的方式来操作一组对象,JDK 内置了这样的元素接口和访问者接口。

如下是元素接口 javax.lang.model.element.Element 的部分代码:

public interface Element extends javax.lang.model.AnnotatedConstruct {
    <R, P> R accept(ElementVisitor<R, P> v, P p);
}

如下是访问者接口 javax.lang.model.element.ElementVisitor 的部分代码:

public interface ElementVisitor<R, P> {
    R visit(Element e, P p);

    default R visit(Element e) {
        return visit(e, null);
    }
}