设计模式之美26--访问者模式,单分派,双分派

本文深入探讨访问者模式,一种设计模式,用于解耦操作与对象,允许一系列操作应用于不同类型对象而不改变对象结构。通过三个版本的代码示例,展示了如何在增加功能时保持代码的可读性和可维护性。

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

定义: 允许一个或者多个操作应用到一组对象上,解耦操作和对象本身。

一般来说,访问者模式针对的是一组类型不同的对象(PdfFile、PPTFile、WordFile)。不过,尽管这组对象的类型是不同的,但是,它们继承相同的父类(ResourceFile)或者实现相同的接口。在不同的应用场景下,我们需要对这组对象进行一系列不相关的业务操作(抽取文本、压缩等),但为了避免不断添加功能导致类(PdfFile、PPTFile、WordFile)不断膨胀,职责越来越不单一,以及避免频繁地添加功能导致的频繁代码修改,我们使用访问者模式,将对象与操作解耦,将这些业务操作抽离出来,定义在独立细分的访问者类(Extractor、
Compressor)中。
在这里插入图片描述

需求; 有三种:TXT、PPT。我们现在要开发一个工具来处理这批资源文件。这个工具的
其中一个功能是,把这些资源文件中的文本内容抽取出来放到word文件中。

版本1

package vistor.version_1;

public abstract class ResourceFile {
    protected String filePath;
    public ResourceFile(String filePath) {
        this.filePath = filePath;
    }
    public abstract void extract2txt();
}

 class PPTFile extends ResourceFile{


    public PPTFile(String filePath) {
        super(filePath);
    }

    @Override
    public void extract2txt() {
        System.out.println("ppt 解析");
    }
}

class TxtFile extends ResourceFile {

    public TxtFile(String filePath) {
        super(filePath);
    }

    @Override
    public void extract2txt() {
        System.out.println("txt 解析");
    }
}

  public static void main(String[] args) {
       List<ResourceFile> resourceFiles = listAllResourceFiles();
       for (ResourceFile resourceFile : resourceFiles) {
           resourceFile.extract2txt();
       }
   }
    private static List<ResourceFile> listAllResourceFiles() {
       List<ResourceFile> resourceFiles = new ArrayList<>();
       resourceFiles.add(new PPTFile("a.pptfile"));
       resourceFiles.add(new TxtFile("TxtFile"));
       return resourceFiles;
    }


版本2

package vistor.version_2;

public abstract class ResourceFile_V2 {
    protected String filePath;
    public ResourceFile_V2(String filePath) {
        this.filePath = filePath;
    }
    public abstract void accept(Extractor extractor);
}

class PPTFile_V2 extends ResourceFile_V2{

    public PPTFile_V2(String filePath) {
        super(filePath);
    }

    @Override
    public void accept(Extractor extractor) {
        extractor.extract2ppt(this);
    }
}

class TxtFile_V2 extends ResourceFile_V2{

    public TxtFile_V2(String filePath) {
        super(filePath);
    }

    @Override
    public void accept(Extractor extractor) {
       extractor.extract2txt(this);
    }
}

public class Extractor {
    public void extract2ppt(PPTFile_V2 pptFile) {
//...
        System.out.println("Extract PPT.");
    }


    public void extract2txt(TxtFile_V2 txtFile_v2) {
//...
        System.out.println("Extract txt.");
    }
}


   private static void test_v2(){
        Extractor extractor = new Extractor();
        List<ResourceFile_V2> resourceFiles = listAllResourceFileV2s();
        for (ResourceFile_V2 resourceFile : resourceFiles) {
            resourceFile.accept(extractor);
        }
    }

    private static List<ResourceFile_V2> listAllResourceFileV2s() {
        List<ResourceFile_V2> resourceFiles = new ArrayList<>();
//        resourceFiles.add(new PPTFile_V2("a.pptfile"));
//        resourceFiles.add(new TxtFile_V2("TxtFile"));
        return resourceFiles;
    }

版本3
如果工具的功能不停地扩展,不仅要能抽取文本内容,还要支持压缩、提取文件元信息(文件名、大小、更新时间等等)构建索引等一系列的功能,那如果我们继续按照上面的实现思路,就会存在这样几个问题:

  1. 违背开闭原则,添加一个新的功能,所有类的代码都要修改;
  2. 虽然功能增多,每个类的代码都不断膨胀,可读性和可维护性都变差了;
  3. 把所有比较上层的业务逻辑都耦合到PdfFile、PPTFile、WordFile类中,导致这些类的职责不够单一,变成了大杂烩。
public interface Vistor {

    void accept(PPTFile_V3 pptFile_v3);

    void accept(TxtFile_V3 txtFile_v3);
}
// 抽取功能
class Extractor_v3 implements Vistor{

    @Override
    public void accept(PPTFile_V3 pptFile_v3) {
        System.out.println("Extract PPT");
    }

    @Override
    public void accept(TxtFile_V3 txtFile_v3) {
        System.out.println("Extract TXT");
    }
}
// 压缩功能
class Compomser_V3 implements Vistor{

    @Override
    public void accept(PPTFile_V3 pptFile_v3) {
        System.out.println("Compomser PPT");
    }

    @Override
    public void accept(TxtFile_V3 txtFile_v3) {
        System.out.println("Compomser TXT");
    }
}

public abstract class ResourceFile_V3 {
    public abstract void accept(Vistor vistor);
}

class PPTFile_V3 extends ResourceFile_V3 {

    @Override
    public void accept(Vistor vistor) {
        vistor.accept(this);
    }
}

class TxtFile_V3 extends ResourceFile_V3 {

    @Override
    public void accept(Vistor vistor) {
        vistor.accept(this);
    }
}

public class ToolApplication3 {
    public static void main(String[] args) {
        test_v3();
    }

    private static void test_v3() {
        Extractor_v3 extractor = new Extractor_v3();
        List<ResourceFile_V3> resourceFiles = listAllResourceFileV2s();
        for (ResourceFile_V3 resourceFile : resourceFiles) {
            resourceFile.accept(extractor);
            // 文件类型(多态)根据传入的Visitor类型,确定具体是哪个实现类
        }

        Compomser_V3 compomser = new Compomser_V3();
        for (ResourceFile_V3 resourceFile : resourceFiles) {
            resourceFile.accept(compomser);
        }
    }

    private static List<ResourceFile_V3> listAllResourceFileV2s() {
        List<ResourceFile_V3> resourceFiles = new ArrayList<>();
        resourceFiles.add(new PPTFile_V3());
        resourceFiles.add(new TxtFile_V3());
        return resourceFiles;
    }
}

在这里插入图片描述

在这里插入图片描述

运用到了多态和重载

单分派,双分派
所谓Single Dispatch,指的是执行哪个对象的方法,根据对象的运行时类型来决定;执行对象的哪个方法,根据方法参数的编译时类型来决定。
所谓Double Dispatch,指的是执行哪个对象的方法,根据对象的运行时类型来决定;执行对象的哪个方法,根据方法参数的运行时类型
来决定。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值