十七、访问者模式
本文最后更新于 1270 天前,其中的信息可能已经有所发展或是发生改变。

Visitor pattern

因为它难理解、难实现,应用它会导致代码的可读性、可维护性变差,所以,访问者模式在实际的软件开发中很少被用到,在没有特别必要的情况下,建议你不要使用访问者模式。

——设计模式之美

前言

这个模式侧重代码实现,主要是解决分离业务代码,抽取能力带来的问题。

示例V1

模拟读取不同格式的文件。

TextFileV1

ResourceFileV1:资源文件抽象类

定义文件相关的操作

public abstract class ResourceFileV1 {
    private String path;

    public ResourceFileV1(String path) {
        this.path = path;
    }

    public abstract void read();
}

PdfFileV1:Pdf文件处理类

public class PdfFileV1 extends ResourceFileV1 {

    public PdfFileV1(String path) {
        super(path);
    }

    @Override
    public void read() {
        System.out.println("读取Pdf文件内容");
    }
}

TextFileV1:文本文件处理类

public class TextFileV1 extends ResourceFileV1 {

    public TextFileV1(String path) {
        super(path);
    }

    @Override
    public void read() {
        System.out.println("读取Text文件内容");
    }
}

使用

    @Test
    public void testV1() {
        final List<ResourceFileV1> resourceFileV1s = buildResourceFiles();
        resourceFileV1s.forEach(ResourceFileV1::read);
    }

    public List<ResourceFileV1> buildResourceFiles() {
        return Lists.newArrayList(new PdfFileV1("xx"),
                                  new TextFileV1("xx"));

    }

存在的问题

如果需求简单,就是针对不同格式的文件简单处理,那上诉设计是没问题的。

但是如果处理操作很多,文件类处理处理文件还有其他业务代码,这时就会造成文件类臃肿,不符合单一职责原则。每增加一种文件处理操作,就需要修改上诉所有类,不符合开闭原则(这句话不是那么绝对,所有原则都要视情况而定)。

重构优化V2

将文件处理能力独立出来,利用重载来处理各种格式的文件。

PdfFileV2

Reader:读取文件

public class Reader {

    public void read(PdfFileV2 pdfFileV2) {
        System.out.println("读取Pdf文件内容");
    }

    public void read(TextFileV2 textFileV2) {
        System.out.println("读取Pdf文件内容");
    }
}

使用

@Test
public void testV2() {
    final List<ResourceFileV2> resourceFileV2s = buildResourceFilesV2();
    resourceFileV2s.forEach(Reader::read);
}

编译前第4行报错

image-20211011112719662

原因是方法重载需要在编译时确认,不能像多态那样,运行时确认。

重构优化V3

不能通过resourceFile确定调用哪个重载方法,但是在PdfFile里是能确定的。

TextFileV3

PdfFileV3

public class PdfFileV3 extends ResourceFileV3 {

    public PdfFileV3(String path) {
        super(path);
    }

    @Override
    public void read(ReaderV3 readerV3) {
        readerV3.read(this);
    }

}

使用

    @Test
    public void testV3() {
        final ReaderV3 readerV3 = new ReaderV3();
        final List<ResourceFileV3> resourceFileV3s = buildResourceFilesV3();
        resourceFileV3s.forEach(resourceFileV3 -> resourceFileV3.read(readerV3));
    }

存在的问题

这是不是和重构前的V1版本很像,并且每增加一种文件操作都得修改所有文件类。

CompressV3:压缩文件

public class CompressV3 {

    public void compress(PdfFileV3 pdfFileV3) {
        System.out.println("压缩Pdf文件");
    }

    public void compress(TextFileV3 textFileV2) {
        System.out.println("压缩Pdf文件");
    }
}

PdfFileV3:增加压缩方法

public class PdfFileV3 extends ResourceFileV3 {

    public PdfFileV3(String path) {
        super(path);
    }

    @Override
    public void read(ReaderV3 readerV3) {
        readerV3.read(this);
    }

    @Override
    public void compress(CompressV3 compressV3) {
        compressV3.compress(this);
    }

}

我们是在白费功夫吗?肯定不是,咋们接着优化。

仔细看看这两个能力类,都只有一个能力方法,可否抽象下,解决文件类使用能力时需新增方法的问题。

重构优化V4

PdfFileV4

VisitorV4:访问者接口

访问不同格式的文件

public interface VisitorV4 {
    void visit(PdfFileV4 pdfFileV4);

    void visit(TextFileV4 textFileV4);
}

ReaderV4:读取文件

public class ReaderV4 implements VisitorV4 {

    @Override
    public void visit(PdfFileV4 pdfFileV4) {
        System.out.println("读取Pdf文件内容");
    }

    @Override
    public void visit(TextFileV4 textFileV4) {
        System.out.println("读取Pdf文件内容");
    }

}

CompressV4:压缩文件

public class CompressV4 implements VisitorV4 {
    @Override
    public void visit(PdfFileV4 pdfFileV4) {
        System.out.println("压缩Pdf文件");
    }

    @Override
    public void visit(TextFileV4 textFileV4) {
        System.out.println("压缩Pdf文件");
    }
}

ResourceFileV4:资源文件抽象父类

public abstract class ResourceFileV4 {
    private String path;

    public ResourceFileV4(String path) {
        this.path = path;
    }

    public abstract void visit(VisitorV4 visitorV4);
}

PdfFileV4:Pdf文件处理类

public class PdfFileV4 extends ResourceFileV4 {

    public PdfFileV4(String path) {
        super(path);
    }

    @Override
    public void visit(VisitorV4 visitorV4) {
        visitorV4.visit(this);
    }
}

TextFileV4:文本文件处理类

public class TextFileV4 extends ResourceFileV4 {

    public TextFileV4(String path) {
        super(path);
    }

    @Override
    public void visit(VisitorV4 visitorV4) {
        visitorV4.visit(this);
    }
}

使用

@Test
public void testV4() {
    final ReaderV4 readerV4 = new ReaderV4();
    final CompressV4 compressV4 = new CompressV4();
    final List<ResourceFileV4> resourceFileV4s = buildResourceFilesV4();
    resourceFileV4s.forEach(resourceFileV4 -> {
        resourceFileV4.visit(readerV4);
        resourceFileV4.visit(compressV4);
    });
}

总结

访问者模式的演进过程复杂,直接看代码很难理解用意。而且从使用场景来看,使用策略模式也能达到目的,并且更好理解。

作者:Yuyy
博客:https://yuyy.info

评论

  1. Yuyy
    博主
    Linux Chrome
    2年前
    2022-10-08 17:03:34

    Golang 实现访问者模式:GO 编程模式:K8S VISITOR 模式——陈皓

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇