访问者模式(Visitor Pattern)

本文通过小学生和初中生计算圆和长方形面积的例子,阐述了访问者模式的概念,展示了如何使用访问者模式在不修改原有数据结构的基础上,为对象结构中的元素添加新的功能。访问者模式具有良好的扩展性、复用性和灵活性,但也存在如违反迪米特原则和开闭原则的问题。代码示例展示了具体访问者和元素的实现,以及对象结构的遍历。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

  • 缺点
    1、具体元素对访问者公布细节,违反了迪米特原则。
    2、增加新的元素类很困难。在访问者模式中,每增加一个新的元素类,都要在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”。
    3、违反了依赖倒置原则,依赖了具体类,没有依赖抽象。

  • 访问者模式包含以下角色
    抽象访问者:定义一个访问具体元素的接口,为每个具体元素类对应一个访问操作 visit() ,该操作中的参数类型标识了被访问的具体元素。
    具体访问者:实现抽象访问者角色中声明的各个访问操作,确定访问者访问一个元素时该做什么。
    抽象元素:声明一个包含接受操作 accept() 的接口,被接受的访问者对象作为 accept() 方法的参数。
    具体元素:实现抽象元素角色提供的 accept() 操作,其方法体通常都是 visitor.visit(this) ,另外具体元素中可能还包含本身业务逻辑的相关操作。
    对象结构:是一个包含元素角色的容器,提供让访问者对象遍历容器中的所有元素的方法,通常由 List、Set、Map 等聚合类实现。

  • 例子:小学生和初中生计算圆和长方形的面积为例,小学生计算后,需要显示计算过程和结果,而初中生计算后只需要显示结果,类图如下
    在这里插入图片描述

  • 代码实现

*抽象访问者

public interface Visitor {
    void visit(Rectangle elementA);
    void visit(Circle elementB);
}

具体访问者

/**
 * 小学生需要显示具体计算过程
 */
public class ConcreteVisitorA implements Visitor {
    @Override
    public void visit(Rectangle elementA) {
        System.out.println("计算公式:长方形面积=长×宽");
        elementA.calculateArea();
    }

    @Override
    public void visit(Circle elementB) {
        System.out.println("计算公式:圆面积 = πr² ");
        elementB.calculateArea();
    }
}
/**
 * 初中生直接显示计算结果
 */
public class ConcreteVisitorB implements Visitor {
    @Override
    public void visit(Rectangle elementA) {
        elementA.calculateArea();
    }

    @Override
    public void visit(Circle elementB) {
        elementB.calculateArea();
    }
}

抽象元素

public interface Element {
    // 接收访问者
    void accept(Visitor visitor);
    // 计算面积
    void calculateArea();
}

具体元素

/**
 * 圆
 */
public class Circle implements Element {
    // 半径
    private double radius;

    public Circle(double radius) {
        this.radius = radius;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    @Override
    public void calculateArea() {
        System.out.println("圆形面积是:" + Math.PI * radius * radius);
    }
}
/**
 * 长方形
 */
public class Rectangle implements Element {
    // 长
    private double length;
    // 宽
    private double wide;

    public Rectangle(double length, double wide) {
        this.length = length;
        this.wide = wide;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
    }
    @Override
    public void calculateArea() {
        System.out.println("长方形面积是:" + length * wide);
    }
}

对象结构

public class ObjectStructure {
    private List<Element> elements = new ArrayList<>();

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

    public void addElement(Element element){
        elements.add(element);
    }

    public void removeElement(Element element){
        elements.remove(element);
    }
}

测试

public class Test {
    public static void main(String[] args) {
        ObjectStructure objectStructure = new ObjectStructure();
        objectStructure.addElement(new Rectangle(5,6));
        objectStructure.addElement(new Circle(2));
        Visitor visitorA = new ConcreteVisitorA();
        Visitor visitorB = new ConcreteVisitorB();
        System.out.println("------" + "小学生" + "------");
        objectStructure.accept(visitorA);
        System.out.println("------" + "初中生" + "------");
        objectStructure.accept(visitorB);
    }
}
// 运行结果
------小学生------
计算公式:长方形面积=长×宽
长方形面积是:30.0
计算公式:圆面积 = πr² 
圆形面积是:12.566370614359172
------初中生------
长方形面积是:30.0
圆形面积是:12.566370614359172
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值