设计模式之 访问者模式 上

访问者模式(Visitor Pattern),意在解耦操作和对象本身。从概念上并不能理解清楚,我们在代码示例中结合讲解。

今天以文件操作为例,来讲解访问者模式。

我们现在要实现多种类型的文件读取功能,比如从word、pdf、excel中读取文件内容加载到程序中。

正常逻辑是实现三个类,分别是ReadWord、ReadPdf、ReadExcel。

代码如下:

1、定义抽象父类

package com.itzhimei.study.design.visitor;

/**
 * @Auther: www.itzhimei.com
 * @Description: 文件读取抽象类
 */
public abstract class ReadFile {

    String filePath;

    public ReadFile(String filePath) {
        this.filePath = filePath;
    }

    /**
     * 读取文件
     */
    public abstract void readFileContent();

}

2、定义三种文件读取功能类

package com.itzhimei.study.design.visitor;

/**
 * @Auther: www.itzhimei.com
 * @Description: 读取word
 */
public class ReadWord extends ReadFile {

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

    @Override
    public void readFileContent() {
        System.out.println("读取Word到程序");
    }
}
package com.itzhimei.study.design.visitor;

/**
 * @Auther: www.itzhimei.com
 * @Description: 读取pdf
 */
public class ReadPdf extends ReadFile {

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

    @Override
    public void readFileContent() {
        System.out.println("读取Pdf到程序");
    }
}
package com.itzhimei.study.design.visitor;

/**
 * @Auther: www.itzhimei.com
 * @Description: 读取excel
 */
public class ReadExcel extends ReadFile {

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

    @Override
    public void readFileContent() {
        System.out.println("读取Excel到程序");
    }
}

用以上代码就能实现我们的需求了,而且按照不同文件,也拆分了不同功能类,但是如果我们不仅仅是从文件中读取内容,还需要其他功能,比如文件内容压缩,那么就意味着,三个功能类,都要改一遍,添加压缩功能方法,也就是说,随着功能增加,我们的代码要频繁修改,这没有达到开闭原则,所以我们要用访问者模式进行改进。

我们在一开始就介绍了访问者模式的特点,那就是将操作和对象解耦。所以我们要将行为抽出来,放在一个单独的类中,我们先对上面的代码进行调整,达到将操作和对象解耦

调整方式是将读取文件内容的功能方法,放到一个外部类Extractor中,由Extractor类来负责实际读取文件的逻辑。看代码:

1、定义抽象文件读取类

package com.itzhimei.study.design.visitor;

/**
 * @Auther: www.itzhimei.com
 * @Description: 文件读取抽象类
 */
public abstract class ReadFile {

    String filePath;

    public ReadFile(String filePath) {
        this.filePath = filePath;
    }

    /**
     * 读取文件
     */
    //public abstract void readFileContent();

    public abstract void accept(Extractor extractor);

}

2、定义各种文件读取的具体实现,这里实现了分别定义了ReadWord、ReadPdf和ReadExcel的读取逻辑,这三方方法是从原来的对应类中抽取出来的方法。

这个执行器中采用了方法重载,运行时,根据不同参数,执行不同的方法。

package com.itzhimei.study.design.visitor;

/**
 * @Auther: www.itzhimei.com
 * @Description: 文件读取执行器
 */
public class Extractor {

    public void readFileContent(ReadWord word) {
        System.out.println("读取Word到程序");
    }

    public void readFileContent(ReadPdf word) {
        System.out.println("读取Pdf到程序");
    }

    public void readFileContent(ReadExcel word) {
        System.out.println("读取Excel到程序");
    }
}

3、定义三种文件读取功能类,原来的类中的readFileContent方法已经不需要了,替换的方法是:accept(Extractor extractor),这里采用组合的模式,在ReadWord的accept方法,使用了Extractor,由Extractor在运行时决定执行哪个方法。

package com.itzhimei.study.design.visitor;

/**
 * @Auther: www.itzhimei.com
 * @Description: 读取word
 */
public class ReadWord extends ReadFile {

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

    /*@Override
    public void readFileContent() {
        System.out.println("读取Word到程序");
    }*/

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

}
package com.itzhimei.study.design.visitor;

/**
 * @Auther: www.itzhimei.com
 * @Description: 读取pdf
 */
public class ReadPdf extends ReadFile {

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

    /*@Override
    public void readFileContent() {
        System.out.println("读取Pdf到程序");
    }*/

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

}
package com.itzhimei.study.design.visitor;


import java.util.concurrent.Executors;

/**
 * @Auther: www.itzhimei.com
 * @Description: 读取excel
 */
public class ReadExcel extends ReadFile {

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

    /*@Override
    public void readFileContent() {
        System.out.println("读取Excel到程序");
    }*/

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

}

4、测试

package com.itzhimei.study.design.visitor;

import java.util.ArrayList;
import java.util.List;

/**
 * @Auther: www.itzhimei.com
 * @Description:
 */
public class Client {

    public static void main(String[] args) {
        Extractor extractor = new Extractor();
        List<ReadFile> rfs = new ArrayList<>();
        rfs.add(new ReadWord("文件路径"));
        rfs.add(new ReadPdf("文件路径"));
        rfs.add(new ReadExcel("文件路径"));

        rfs.forEach(x -> x.accept(extractor));
    }
}

输出:

读取Word到程序
读取Pdf到程序
读取Excel到程序

类结构图:

到这里,第二个版本已经是一个访问者模式了,我们在下一节来演示一下,基于访问者模式的多功能扩展,也就是上面说的再扩展一个文件内容压缩功能。