【Java教程】Day20-23 设计模式:行为型模式——访问者模式

访问者模式(Visitor Pattern)是一种行为型设计模式,它通过将操作从元素结构中抽离出来,允许我们在不修改元素结构的情况下,向其中添加新的操作。访问者模式适用于以下场景:当你希望在不改变对象结构的情况下,向这些对象结构中的元素添加新的操作时,使用该模式能够实现更高的扩展性。


1. 访问者模式简介

访问者模式的主要目的是使得客户端能够在不改变对象结构的前提下,对其进行新的操作。它适用于对象结构相对稳定,但需要频繁增加新操作的场景。该模式的核心思想是:将数据结构与操作分离,通过将操作封装成访问者对象,进行灵活的扩展。

1.1 访问者模式的组成部分

  • Visitor(访问者接口):声明了具体访问者所需的操作,通常每个操作方法对应元素结构中的一个类型。

  • ConcreteVisitor(具体访问者):实现了访问者接口,定义具体操作。

  • Element(元素接口):定义了接受访问者的方法,通常包括一个accept()方法,允许访问者对元素进行操作。

  • ConcreteElement(具体元素):实现了元素接口,定义了具体的业务逻辑。

  • ObjectStructure(对象结构):通常是由多个元素组成的结构(如树形结构),提供一个accept()方法来传入访问者对象。


2. 访问者模式的结构

访问者模式通过双重分派的方式,将操作从元素中提取出来。访问者对象与元素结构解耦,并通过回调的方式来执行相应的操作。下面是访问者模式的基本结构:

arduino          ┌───────────────┐          │    Client     │          └──────┬────────┘            ┌────┴─────┐            │  Visitor │            └────┬─────┘        ┌────────┴────────┐        │        │        │┌───────┴──────┐ ┌───────┴──────┐│ConcreteVisitorA│ │ConcreteVisitorB│└────────────────┘ └────────────────┘  ┌─────┴─────┐  │ ObjectStructure │  └─────┬─────┘   ┌────┴──────┐   │   Element │   └────┬──────┘  ┌─────┴─────┐  │ConcreteElementA│  │ConcreteElementB│  └────────────────┘

 


3. 访问者模式的应用

3.1 问题背景

假设我们需要递归遍历某个文件夹中的所有子文件夹和文件,并找出所有.java文件。传统的递归方式将文件夹遍历和文件操作混在一起,若后续需要新增操作(例如清理.class文件),则必须重复写遍历逻辑。

3.2 使用访问者模式改写

我们可以利用访问者模式,将数据结构与操作逻辑分离,实现对文件系统的操作更加灵活。

步骤一:定义访问者接口

访问者接口定义了能够对文件夹和文件进行的操作:

javapublic interface Visitor {    void visitDir(File dir);  // 访问文件夹    void visitFile(File file); // 访问文件}

 

步骤二:定义文件结构类

FileStructure类用于存储文件夹信息,并且能够通过handle()方法处理传入的访问者。

javapublic class FileStructure {    private File path;    public FileStructure(File path) {        this.path = path;    }    public void handle(Visitor visitor) {        scan(this.path, visitor);    }    private void scan(File file, Visitor visitor) {        if (file.isDirectory()) {            visitor.visitDir(file);            for (File sub : file.listFiles()) {                scan(sub, visitor);            }        } else if (file.isFile()) {            visitor.visitFile(file);        }    }}

 

步骤三:定义具体访问者

1. 查找.java文件的访问者:

javapublic class JavaFileVisitor implements Visitor {    public void visitDir(File dir) {        System.out.println("Visiting directory: " + dir);    }    public void visitFile(File file) {        if (file.getName().endsWith(".java")) {            System.out.println("Found Java file: " + file);        }    }}

 

2. 清理.class文件的访问者:

javapublic class ClassFileCleanerVisitor implements Visitor {    public void visitDir(File dir) {    }    public void visitFile(File file) {        if (file.getName().endsWith(".class")) {            System.out.println("Cleaning class file: " + file);        }    }}

 

步骤四:使用访问者模式

在客户端代码中,我们可以根据需求传入不同的访问者:

javaFileStructure fs = new FileStructure(new File("."));fs.handle(new JavaFileVisitor()); // 查找 .java 文件fs.handle(new ClassFileCleanerVisitor()); // 清理 .class 文件

 

 

 


4. 访问者模式的优势

访问者模式的核心优势是扩展性。我们可以不修改现有的元素结构,而是通过新增访问者来对元素执行新的操作。例如,增加一个清理.xml文件的操作时,只需要定义一个新的访问者,而不需要修改文件夹结构类。


5. Java标准库中的访问者模式

Java标准库中的Files.walkFileTree()方法实现了一个典型的访问者模式。以下是使用Files.walkFileTree()遍历文件夹的示例:

javaimport java.io.*;import java.nio.file.*;import java.nio.file.attribute.*;public class Main {    public static void main(String[] args) throws IOException {        Files.walkFileTree(Paths.get("."), new MyFileVisitor());    }}class MyFileVisitor extends SimpleFileVisitor<Path> {    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {        System.out.println("Visiting directory: " + dir);        return FileVisitResult.CONTINUE;    }    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {        System.out.println("Visiting file: " + file);        return FileVisitResult.CONTINUE;    }}

 

Files.walkFileTree()方法允许我们使用自定义的FileVisitor类处理目录和文件。通过返回FileVisitResult.CONTINUE,我们可以继续访问,或者通过FileVisitResult.TERMINATE停止访问。

 


6. 小结

访问者模式通过将操作逻辑抽象为访问者对象,能够在不改变数据结构的情况下扩展操作。它适用于需要在一组复杂对象上执行不同操作的场景,尤其是当操作变化频繁时,使用访问者模式能够提高系统的灵活性和扩展性。

 

 

 

 

### Java IO流使用教程及常见问题 #### 文件字节输入流的应用场景与实现方法 对于文件的读取操作,`FileInputStream` 是一种常用的字节输入流。它能够逐个字节地从文件中读取数据。在具体应用时,通常会先建立一个 `FileInputStream` 对象来连接目标文件,之后利用其提供的 `read()` 方法获取单个字节的数据并将其转换成相应的字符形式打印出来直到遇到文件结束符 `-1` 为止[^3]。 ```java public class FileInputStreamDemo01 { public static void main(String[] args) throws Exception { InputStream is = new FileInputStream("day09-oop/src/data.txt"); int b; while ((b = is.read()) != -1){ System.out.print((char) b); } is.close(); } } ``` #### 对象反序列化的流程解析 当涉及到复杂对象而非单纯文本或二进制数据的时候,则需要用到更为高级的功能——对象串行化与反串行化。这里给出的对象反序列化例子展示了如何通过 `ObjectInputStream` 类加载存储于磁盘上的对象副本到内存之中。需要注意的是,在执行此过程前需确保所要恢复的对象实现了 `Serializable` 接口以便支持此类操作;另外就是路径设置应当准确无误以指向正确的文件位置[^1]。 ```java import java.io.*; public class Demo07 { public static void main(String[] args) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\itcast\\oos.txt")); Object obj = ois.readObject(); Student s = (Student) obj; System.out.println(s.getName() + "," + s.getAge()); ois.close(); } } ``` #### 输入输出流分类及其作用范围说明 按照功能划分,Java 的 IO 流主要被区分为两大类别:节点流(Node Stream)以及处理流/包装流(Processing Stream / Wrapper Stream)。前者负责直接访问外部设备如硬盘中的文件完成基本的数据传输任务;后者则是在已有基础上添加额外特性比如缓冲机制或是编码转换等功能从而简化编程工作量提升效率。整个体系结构围绕着四个核心抽象基类构建而成即 `InputStream`, `OutputStream`, `Reader`, 和 `Writer` 及它们各自的子类群组形成了一套完整的解决方案框架用于满足各种不同的应用场景需求[^5]。 #### 文本文件读写的简易指南 针对简单的纯文本文件而言,除了上述提到过的基于字节数组的方式外还可以采用专门设计用来处理 Unicode 字符集的 Reader 和 Writer 家族成员来进行高效便捷的操作。这类工具允许开发者一次性读入整行甚至多行文字内容而无需关心底层具体的位模式表示细节因此非常适合初学者快速入门掌握基础技能[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值