35 设计模式:访问者模式-行为型模式

240 阅读6分钟

行为型模式之:

  • 观察者模式(Observer) ✅ 重要程度:⭐️⭐️⭐️⭐️⭐️

  • 迭代器模式(Iterator) ✅ 重要程度:⭐️⭐️⭐️⭐️⭐️

  • 策略模式(Strategy) ✅ 重要程度:⭐️⭐️⭐️⭐️

  • 模板方法模式(Template Method) ✅ 重要程度:⭐️⭐️⭐️

  • 职责链模式(Chain of Responsibility) ✅ 重要程度:⭐️⭐️⭐️

  • 访问者模式(Visitor) ✅ 重要程度:⭐️

  • 命令模式(Command)
    重要程度:⭐️⭐️⭐️⭐️

  • 解释器模式(Interpreter)
    重要程度:⭐️

  • 中介者模式(Mediator)
    重要程度:⭐️⭐️

  • 备忘录模式(Memento)
    重要程度:⭐️⭐️

  • 状态模式(State)
    重要程度:⭐️⭐️⭐️

1.什么是访问者模式

访问者模式是一种行为设计模式,用于在不改变数据结构的前提下定义作用于该数据结构元素的新操作。该模式将数据结构和操作分离,使得数据结构的变化不会影响到数据操作的变化,从而达到解耦的目的。

在访问者模式中,数据结构通常由一个稳定的对象结构(如树、列表等)组成,而对这个对象结构进行操作的操作集合被称为访问者,这些操作可以是新增的操作或者对数据结构中元素的各种不同操作。访问者模式的核心思想是让数据结构的每个元素接受访问者的访问,从而实现对元素的操作。

举个例子,假设有一个文件系统,文件系统中包含文件(File)和文件夹(Folder)两种元素,文件夹中可能还包含其他文件夹或文件。现在需要对文件系统中的所有文件和文件夹进行操作,比如统计文件数、计算文件总大小等。

使用访问者模式,可以定义一个 Visitor 接口,该接口包含不同类型的 visit 方法,比如 visitFile、visitFolder 等。然后针对文件和文件夹分别实现相应的 visit 方法,用于执行具体的操作。文件和文件夹类都可以接受 Visitor 的访问,然后将自身传递给 Visitor,由 Visitor 执行相应的操作。

通过访问者模式,可以方便地在不改变文件系统结构的情况下,为文件系统增加新的操作,而不会影响到文件系统中已有的操作逻辑。这样做的好处是,系统的可扩展性更强,新增功能的开发更加灵活。

2.怎么实现访问者模式?

// 文件接口
interface FileSystemElement {
    void accept(Visitor visitor);
}

// 文件类
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(Visitor visitor) {
        visitor.visit(this);
    }
}

// 文件夹类
class Folder implements FileSystemElement {
    private String name;
    private List<FileSystemElement> elements;

    public Folder(String name) {
        this.name = name;
        elements = new ArrayList<>();
    }

    public String getName() {
        return name;
    }

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

    @Override
    public void accept(Visitor visitor) {
        visitor.visit(this);
        for (FileSystemElement element : elements) {
            element.accept(visitor);
        }
    }
}

// 访问者接口
interface Visitor {
    void visit(File file);
    void visit(Folder folder);
}

// 具体访问者实现
class SizeVisitor implements Visitor {
    private int totalSize;

    public int getTotalSize() {
        return totalSize;
    }

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

    @Override
    public void visit(Folder folder) {
        // 文件夹大小暂时不计算
    }
}

// 主程序
public class Main {
    public static void main(String[] args) {
        // 创建文件系统
        Folder root = new Folder("Root");
        Folder documents = new Folder("Documents");
        Folder pictures = new Folder("Pictures");

        File file1 = new File("File1.txt", 100);
        File file2 = new File("File2.txt", 200);
        File file3 = new File("Image1.jpg", 300);

        documents.addElement(file1);
        documents.addElement(file2);
        pictures.addElement(file3);

        root.addElement(documents);
        root.addElement(pictures);

        // 创建访问者
        SizeVisitor sizeVisitor = new SizeVisitor();

        // 文件系统接受访问者访问
        root.accept(sizeVisitor);

        // 输出文件系统总大小
        System.out.println("Total size of file system: " + sizeVisitor.getTotalSize() + " bytes");
    }
}

在这个示例中,文件系统包括文件(File)和文件夹(Folder)两种元素,访问者模式通过定义 Visitor 接口和具体的访问者实现(如 SizeVisitor)来访问文件系统中的元素。每个元素(文件或文件夹)都有一个 accept 方法,用于接受访问者的访问,并调用访问者的相应方法进行处理。通过访问者模式,我们可以方便地对文件系统中的元素进行不同的操作,而不需要修改文件和文件夹的类定义。

3.什么时候使用访问者模式呢?

由定义可以知道:访问者模式是一种行为设计模式,用于在不改变数据结构的前提下定义作用于该数据结构元素的新操作。该模式将数据结构和操作分离,使得数据结构的变化不会影响到数据操作的变化,从而达到解耦的目的。

  1. 对一个对象结构中的各种元素进行操作,并且这些操作需要根据不同的元素类型进行不同的处理。

  2. 需要对对象结构中的元素进行操作,但是不希望在每个元素类中添加新的操作方法,以避免违反开闭原则。

  3. 需要在不修改现有类层次结构的情况下,增加新的操作或行为。

  4. Java编程语言的AST解析器:在编译器中,访问者模式经常用于遍历和处理抽象语法树,进行语法分析、语义分析等操作。

  5. Apache Wicket:Wicket是一个用于构建Java Web应用程序的开源框架,其中的组件结构和事件处理机制可以看作是一种访问者模式的应用。

  6. Eclipse JDT(Java Development Tools):Eclipse JDT提供了一组用于Java开发的工具,其中包括了对Java源代码的解析和分析,这些工具在内部使用了访问者模式来处理Java代码的结构和语法。

AST(抽象语法树)解析器是编程语言编译器或解释器中的一个重要组成部分,其作用是将源代码转换为抽象语法树的形式,以便进一步的语法分析、语义分析和代码生成等操作。

具体来说,AST解析器的主要作用包括:

  1. 将源代码转换为抽象语法树(AST): AST是一种用于表示源代码语法结构的树形数据结构,它以抽象的方式描述了源代码中的各种语法元素和它们之间的关系。AST通常由节点(Node)组成,每个节点代表源代码中的一个语法元素,例如表达式、语句、声明等。
  2. 语法分析和语义分析: AST解析器负责对源代码进行语法分析和语义分析,通过识别源代码中的各种语法结构,并构建相应的抽象语法树。在语法分析过程中,AST解析器会检查源代码是否符合语法规则,并进行必要的错误检查和报告。在语义分析过程中,AST解析器会对语法结构进行进一步的分析,以确定其语义和含义,并进行类型检查、作用域分析等操作。
  3. 后续处理和优化: 构建完成抽象语法树后,AST解析器可能会进行一些后续的处理和优化操作,例如常量折叠、表达式简化、代码重构等,以改善程序的性能和可维护性。