设计模式之 访问者模式

访问者模式(Visitor Pattern)是一种行为型设计模式,它允许你在不改变类的前提下,增加新的操作。换句话说,访问者模式使得你能够定义一个新的操作,作用于一个包含不同对象的对象结构上,而无需修改这些对象的类。该模式通过将操作封装在访问者类中,分离了算法与数据结构的结合,使得代码更加灵活和扩展性更强。

访问者模式的核心思想是"通过一个访问者对象对一组对象(元素)进行操作,避免了直接修改这些对象的类。"

一、访问者模式的结构

访问者模式的结构主要由以下几个角色组成:

  1. Visitor(访问者接口):定义所有的访问方法,每个方法对应一个元素类的操作。
  2. ConcreteVisitor(具体访问者):实现访问者接口,定义对每个元素类的具体操作。
  3. Element(元素接口):定义接受访问者的方法。元素类通过该方法来接受访问者的访问。
  4. ConcreteElement(具体元素):实现元素接口,定义一个具体的元素类,可以是集合、树形结构等。
  5. ObjectStructure(对象结构):通常是一个元素的集合,提供一个方法来遍历所有的元素,并将当前元素传递给访问者。
二、访问者模式的工作原理

访问者模式通过将访问者与元素解耦合,从而使得在不修改元素类的情况下可以添加新的操作。访问者模式通过以下几个步骤来实现:

  1. 创建访问者接口(Visitor):访问者接口定义了针对每个具体元素类的访问方法。
  2. 创建具体访问者(ConcreteVisitor):实现访问者接口,提供对每个具体元素的操作。
  3. 创建元素接口(Element) :每个元素类实现此接口,定义接受访问者的 accept() 方法,通常在该方法中调用访问者的访问方法。
  4. 创建具体元素类(ConcreteElement):具体元素类实现元素接口,并定义与访问者的交互逻辑。
  5. 创建对象结构(ObjectStructure):对象结构是一个包含多个元素的集合,可以通过该结构遍历并访问所有元素。

当一个访问者对象访问某个元素时,元素会调用访问者的相应方法,从而使得访问者能够对元素执行某些操作。

三、示例代码

以下是一个简单的访问者模式示例,展示了如何通过访问者模式对一组不同的元素执行操作。

访问者接口
java 复制代码
public interface Visitor {
    void visitDog(Dog dog);

    void visitCat(Cat cat);
}
元素接口
java 复制代码
public interface Animal {
    void accept(Visitor visitor);
}
具体元素类
java 复制代码
public class Dog implements Animal{
    private String name;

    public Dog(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public void accept(Visitor visitor) {
        visitor.visitDog(this);
    }
}
java 复制代码
public class Cat implements Animal{
    private String name;

    public String getName() {
        return name;
    }

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

    public Cat(String name) {
        this.name = name;
    }

    @Override
    public void accept(Visitor visitor) {
        visitor.visitCat(this);
    }
}
具体访问者类
java 复制代码
public class FoodRequirementVisitor implements Visitor{
    @Override
    public void visitDog(Dog dog) {
        System.out.println(dog.getName()+"需要吃饭");
    }

    @Override
    public void visitCat(Cat cat) {
        System.out.println(cat.getName()+"需要吃饭");
    }
}
java 复制代码
public class WaterRequirementVisitor implements Visitor{

    @Override
    public void visitDog(Dog dog) {
        System.out.println(dog.getName()+"需要喝水");
    }

    @Override
    public void visitCat(Cat cat) {
        System.out.println(cat.getName()+"需要喝水");
    }
}
对象结构
java 复制代码
public class Home {
    private List<Animal> list = new ArrayList<>();
    public void addAnimal(Animal animal){
        list.add(animal);
    }

    public void showAnimals(Visitor visitor){
        for (Animal animal : list) {
            animal.accept(visitor);
        }
    }
}
客户端
java 复制代码
public class Client {
    public static void main(String[] args) {
        Cat cat = new Cat("小白");
        Dog dog = new Dog("小黑");
        Home home = new Home();
        home.addAnimal(cat);
        home.addAnimal(dog);

        home.showAnimals(new FoodRequirementVisitor());
        home.showAnimals(new WaterRequirementVisitor());

    }
}
输出结果
四、访问者模式的优缺点
优点:
  1. 可以在不修改元素类的情况下,增加新的操作:访问者模式可以在不改变元素类代码的情况下,增加新的操作或行为,这样可以避免污染原有类的结构,提高了代码的灵活性和可扩展性。
  2. 符合单一职责原则:每个访问者类只负责一个特定的操作,操作与元素的定义是分离的,因此符合单一职责原则。
  3. 简化元素类:元素类不需要定义复杂的操作,操作都由访问者来完成,因此元素类通常会变得更简单。
缺点:
  1. 增加了类的数量:由于每增加一个新的操作,就需要创建一个新的访问者类,这可能会导致类的数量急剧增加,特别是当有很多元素类和操作时。
  2. 元素类需要改变:虽然访问者模式能够在不修改操作的情况下扩展新的操作,但如果有新的元素类加入,所有的访问者类都需要修改,意味着访问者模式并不能完全避免对元素类的修改。
  3. 对元素的结构要求较高 :访问者模式需要将元素的 accept() 方法与访问者的 visit() 方法对接,因此在设计时需要非常谨慎。
五、应用场景

访问者模式通常适用于以下场景:

  1. 需要对一组对象进行操作:当一组不同类型的对象需要执行一系列操作,而这些操作很少变动时,访问者模式非常适用。操作可以由访问者进行封装和实现,从而避免修改这些对象的代码。
  2. 需要定义多个操作,但不想改变元素类:如果需要对对象结构进行多次不同的操作,而且这些操作会随着时间变化,访问者模式提供了一种不修改元素类的方式来增加新的操作。
  3. 复杂的对象结构:当对象结构较为复杂,并且操作多样时,使用访问者模式可以将操作与数据结构分离,从而使得扩展和修改变得更加容易。
相关推荐
java排坑日记1 小时前
poi-tl+kkviewfile实现生成pdf业务报告
java·pdf·word
V+zmm101342 小时前
校园约拍微信小程序设计与实现ssm+论文源码调试讲解
java·微信小程序·小程序·毕业设计·ssm
猿来入此小猿2 小时前
基于SpringBoot小说平台系统功能实现四
java·spring boot·毕业设计·毕业源码·在线小说阅读·在线小说平台·免费学习:猿来入此
Cosmoshhhyyy3 小时前
LeetCode:2274. 不含特殊楼层的最大连续楼层数(排序 Java)
java·算法·leetcode
Dolphin_Home3 小时前
Spring Boot 多环境配置与切换
java·spring boot·后端
我本是机械人3 小时前
MVCC实现原理及其作用
java·数据结构·数据库·后端·mysql·算法
路在脚下@3 小时前
SpringMVC的消息转换器
java·spring boot
sin22013 小时前
springmvc--请求参数的绑定
java·mvc
装不满的克莱因瓶4 小时前
【Redis经典面试题十】热key与大key的问题如何解决?
java·数据库·redis·缓存·面试·面试题·key
武昌库里写JAVA4 小时前
Redis奇幻之旅(四)4. Redis Cluster
java·开发语言·spring boot·学习·课程设计