设计模式:访问者模式

访问者模式(Visitor Pattern)是行为设计模式的一种,它使你能够在不修改对象结构的情况下,给对象结构中的每个元素添加新的功能。访问者模式将数据结构和作用于结构上的操作解耦,使得操作集合可相对自由地演化。

核心概念

  • Element(元素接口/抽象类):定义一个接受访问者访问的接口或抽象类,元素可以是抽象类也可以是接口,通常会有一个accept()方法用于接收访问者对象的访问。

  • ConcreteElement(具体元素类):实现了Element接口或继承了Element抽象类的具体类,每个具体元素都实现了accept()方法,用于调用访问者相应的方法。

  • Visitor(访问者接口):声明了一个或多个访问元素的方法,使得每个元素都能接受访问者的操作。这些方法的名称通常反映了访问者对元素的操作。

  • ConcreteVisitor(具体访问者类):实现了Visitor接口,为每一种ConcreteElement提供具体的操作实现。

应用场景

  • 当需要对一个对象结构中的对象施加多种不同的操作,而这些操作彼此独立,且需要避免让这些操作"污染"这些对象的类时。
  • 对象结构比较稳定,但经常需要在此结构上定义新的操作。

优缺点

优点:

  • 扩展性好:可以在不修改对象结构中的元素的情况下,为对象结构中的元素添加新的功能。
  • 符合单一职责原则:将数据结构和数据操作分离,使得两者可以独立变化。

缺点:

  • 访问者模式使得对象结构变得复杂,增加新的元素类或访问者类都需要修改原有代码,违反了开闭原则。
  • 过多的ConcreteVisitor可能会导致类膨胀,系统难于维护。

以下是使用Java实现访问者模式的一个完整示例。在这个例子中,我们假设有一个简单的文件系统结构,由文件(File)和目录(Directory)组成。我们的目标是实现两种不同的访问者:一个用于计算文件系统的总大小,另一个用于打印文件系统的内容结构。

1. 定义元素接口(Element)

首先,定义一个表示文件系统元素的接口,它包含一个接受访问者的方法。

java 复制代码
// 文件系统元素接口
interface FileSystemElement {
    void accept(FileSystemVisitor visitor);
}

2. 具体元素类(ConcreteElement)

然后,定义具体的文件和目录类,它们都实现了FileSystemElement接口。

java 复制代码
// 文件类
class File implements FileSystemElement {
    private String name;
    private int size;

    public File(String name, int size) {
        this.name = name;
        this.size = size;
    }

    public String getName() {
        return name;
    }

    public int getSize() {
        return size;
    }

    @Override
    public void accept(FileSystemVisitor visitor) {
        visitor.visit(this);
    }
}

// 目录类
class Directory implements FileSystemElement {
    private String name;
    private List<FileSystemElement> elements = new ArrayList<>();

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

    public void add(FileSystemElement element) {
        elements.add(element);
    }

    public String getName() {
        return name;
    }

    @Override
    public void accept(FileSystemVisitor visitor) {
        visitor.visit(this);
    }

    public List<FileSystemElement> getElements() {
        return elements;
    }
}

3. 访问者接口(Visitor)

定义访问者接口,包含访问文件和目录的方法。

java 复制代码
// 访问者接口
interface FileSystemVisitor {
    void visit(File file);
    void visit(Directory directory);
}

4. 具体访问者类(ConcreteVisitor)

创建两个访问者类,一个用于计算总大小,一个用于打印结构。

java 复制代码
// 计算大小的访问者
class SizeCalculatorVisitor implements FileSystemVisitor {
    private int totalSize = 0;

    @Override
    public void visit(File file) {
        totalSize += file.getSize();
    }

    @Override
    public void visit(Directory directory) {
        for (FileSystemElement element : directory.getElements()) {
            element.accept(this);
        }
    }

    public int getTotalSize() {
        return totalSize;
    }
}

// 打印结构的访问者
class StructurePrinterVisitor implements FileSystemVisitor {
    private String prefix = "";

    @Override
    public void visit(File file) {
        System.out.println(prefix + "- " + file.getName() + " (" + file.getSize() + " KB)");
    }

    @Override
    public void visit(Directory directory) {
        System.out.println(prefix + "+ " + directory.getName());
        prefix += "  ";
        for (FileSystemElement element : directory.getElements()) {
            element.accept(this);
        }
        prefix = prefix.substring(2);
    }
}

5. 客户端代码

最后,客户端代码创建文件系统结构并使用访问者。

java 复制代码
public class VisitorPatternDemo {
    public static void main(String[] args) {
        Directory rootDir = new Directory("root");
        Directory subDir = new Directory("subdir");
        File file1 = new File("file1.txt", 100);
        File file2 = new File("file2.txt", 200);

        rootDir.add(file1);
        rootDir.add(subDir);
        subDir.add(file2);

        // 使用大小计算器访问者
        FileSystemVisitor sizeCalculator = new SizeCalculatorVisitor();
        rootDir.accept(sizeCalculator);
        System.out.println("Total size: " + sizeCalculator.getTotalSize() + " KB");

        // 使用结构打印访问者
        System.out.println("\nPrinting structure:");
        FileSystemVisitor structurePrinter = new StructurePrinterVisitor();
        rootDir.accept(structurePrinter);
    }
}

这个例子展示了如何使用访问者模式来添加新的操作(计算大小和打印结构),而不需要修改文件和目录的类。新增操作时,只需添加新的访问者类即可。

相关推荐
苹果醋335 分钟前
React源码02 - 基础知识 React API 一览
java·运维·spring boot·mysql·nginx
Hello.Reader1 小时前
深入解析 Apache APISIX
java·apache
菠萝蚊鸭1 小时前
Dhatim FastExcel 读写 Excel 文件
java·excel·fastexcel
旭东怪2 小时前
EasyPoi 使用$fe:模板语法生成Word动态行
java·前端·word
007php0072 小时前
Go语言zero项目部署后启动失败问题分析与解决
java·服务器·网络·python·golang·php·ai编程
∝请叫*我简单先生2 小时前
java如何使用poi-tl在word模板里渲染多张图片
java·后端·poi-tl
ssr——ssss2 小时前
SSM-期末项目 - 基于SSM的宠物信息管理系统
java·ssm
一棵星2 小时前
Java模拟Mqtt客户端连接Mqtt Broker
java·开发语言
鲤籽鲲2 小时前
C# Random 随机数 全面解析
android·java·c#
zquwei3 小时前
SpringCloudGateway+Nacos注册与转发Netty+WebSocket
java·网络·分布式·后端·websocket·网络协议·spring