设计模式之访问者模式

616 阅读5分钟

这是我参与8月更文挑战的第5天,活动详情查看:8月更文挑战

欢迎来到今天的学习,今天我们一起来学习下访问者代理模式。多唠叨几句,本月将会对java的设计模式精讲,欢迎点击头像,关注我的专栏,我会持续更新,加油!

系列文章:

设计模式之单例模式

设计模式之工厂模式

设计模式之建造者模式

设计模式之代理模式

...持续更新中

话不多说,进入正题

访问者模式

个人认为该模式在实际开发中运用并不是很多,但是一旦要用到了,那便是无可替代的。

访问者模式的原始定义是:访问者模式是一种将数据操作和数据结构分离的设计模式。允许在运行时将一个或多个操作应用于一组对象,将操作与对象结构分离。

访问者模式核心关注点是分离一组对象结构和对象的操作,对象结构可以各不相同,但必须以某一个或一组操作作为连接的中心点。换句话说,访问者模式是以行为(某一个操作)作为扩展对象功能的出发点,在不改变已有类的功能的前提下进行批量扩展。

听起来是不是很抽象,当然这是很官方,很专业的词来解释,这理解起来可能费点劲,但是我今天会用最通俗的的场景和代码来帮助你理解和学习。(当你看完我下面的例子讲解之后,你回来再看这段话,便想说SO DE SI NE)

下面来看张图(图片来源于网络)

image.png

针对上图我们做一下介绍:

  • Visitor接口或者抽象类,定义了访问者类型访问的类型和行为,它的参数就是被访问的元素,它的方法个数理论上与元素的个数是一样的,因此,访问者模式要求元素的类型要稳定,如果经常添加、移除元素类,必然会导致频繁地修改 Visitor 接口,如果出现这种情况,则说明不适合使用访问者模式。 注意这里,如果经常添加、移除元素类,必然会导致频繁地修改 Visitor 接口,如果出现这种情况,则说明不适合使用访问者模式,那样便违背了迪米特法则与开放原则。

  • Element:元素接口或者抽象类,它定义了一个接受访问者(accept)的方法,其意义是指每一个元素都要可以被访问者访问。

注意这里,这里可以代指访问者的入口,可以理解成访问者,从这个方法内去分别访问不同的对象,不同的元素

  • ElementA、ElementB:具体的元素类,它提供接受访问的具体实现,而这个具体的实现,通常情况下是使用访问者提供的访问该元素类的方法

注意这里,说通俗点,就是接口的实现类,此元素被访问到完全是根据调用者的参数来决定

  • ObjectStructure 定义当中所提到的对象结构,对象结构是一个抽象表述,它内部管理了元素集合,并且可以迭代这些元素提供访问者访问。

注意这里,我们知道,实际工作当中,可能会有若干个元素类,那么ObjectStructure便是管理这些元素类的。

如果你看到这里还是依然觉得很抽象,便看下面的代码示例!

代码示例

我们想象这样一种场景,面试官(CTO)和研发人员(技术总监面试技术开发),当然还有我们的产品经理面试产品助理。研发人员对于CTO而言,CTO主要在意面试者的技术经验,而产品经理(CPO)在意产品助理的产品的经验

那么问题来了,这两者的关注点是不一样的。这就需要技术总监和产品经理(CPO)两种职位进行不同的处理。访问者模式此时可以派上用场了。


//面试者
public abstract class Interviewers {
    
    //面试者姓名
    public String name;
    public int experience ;// 面试者经验(暂时用工作年限作为直接经验)

    public Interviewers(String name) {
        this.name = name;
        experience = new Random().nextInt(10);
    }
    // 核心方法,接受Visitor的访问
    public abstract void accept(Visitor visitor);
}

Interviewers 类定义了面试者基本信息及一个 accept 方法,accept 方法表示接受访问者的访问,由子类具体实现。Visitor 是个接口,传入不同的实现类,可访问不同的数据。下面看看技术开发工程师和产品助理的代码:

// 工程师
public class Engineer extends Interviewers {

    public Engineer(String name) {
        super(name);
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    // 工程师经验值(为代码量)
    public int getCodeLines() {
        return new Random().nextInt(10 * 10000);
    }
}
//产品助理
public class Product extends Interviewers {

    public Product(String name) {
        super(name);
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    // 产品助理产品数量
    public int getProducts() {
        return new Random().nextInt(10);
    }
}

工程师是代码数量,产品助理是产品数量,他们的职责不一样,也就是因为差异性,才使得访问模式能够发挥它的作用。Interviewers、Engineer、Product 3个类型就是对象结构,这些类型相对稳定,不会发生变化。

下面看看 Visitor 类型的定义, Visitor 声明了两个 visit 方法,分别是对工程师和经理对访问函数,具体代码如下:

public interface Visitor {

    // 访问工程师类型
    void visit(Engineer engineer);

    // 产品助理类型
    void visit(Product product);
}

首先定义了一个 Visitor 接口,该接口有两个 visit 函数,参数分别是 Engineer、Product,也就是说对于 Engineer、Product 的访问会调用两个不同的方法,以此达成区别对待、差异化处理。具体实现类为 CTOVisitor、CPOVisitor类,具体代码如下:

// CTO访问者
public class CTOVisitor implements Visitor {
    @Override
    public void visit(Engineer engineer) {
        System.out.println("开发工程师: " + engineer.name + ", 年限和代码量: " + engineer.getProducts);
    }

    @Override
    public void visit(Product product) {
        System.out.println("产品助理: " + product.name + ", KPI: " + manager.kpi +
                ", 新产品数量: " + product.getProducts());
    }
}

OK,到这里就结束