设计模式之美
王争
前 Google 工程师,《数据结构与算法之美》专栏作者
123426 人已学习
新⼈⾸单¥98
登录后,你可以任选6讲全文学习
课程目录
已完结/共 113 讲
设计模式与范式:行为型 (18讲)
设计模式之美
15
15
1.0x
00:00/00:00
登录|注册

68 | 访问者模式(上):手把手带你还原访问者模式诞生的思维过程

可读性差
代码实现难理解
满足开闭原则
保持类职责单一
解耦操作和对象本身
其他实现思路
缺点
优点
避免频繁代码修改
避免类不断膨胀,职责越来越不单一
需要对这组对象进行一系列不相关的业务操作
一组类型不同的对象,但继承相同的父类或实现相同的接口
项目中应用可能导致代码可读性差
调用类的哪个重载函数由参数的声明类型决定
函数重载是静态绑定
应对代码的复杂性
满足开闭原则
保持类职责单一
解耦操作和对象本身
允许一个或者多个操作应用到一组对象上
课堂讨论
优缺点
应用场景
代码实现难点
原理
访问者模式

该思维导图由 AI 生成,仅供参考

前面我们讲到,大部分设计模式的原理和实现都很简单,不过也有例外,比如今天要讲的访问者模式。它可以算是 23 种经典设计模式中最难理解的几个之一。因为它难理解、难实现,应用它会导致代码的可读性、可维护性变差,所以,访问者模式在实际的软件开发中很少被用到,在没有特别必要的情况下,建议你不要使用访问者模式。
尽管如此,为了让你以后读到应用了访问者模式的代码的时候,能一眼就能看出代码的设计意图,同时为了整个专栏内容的完整性,我觉得还是有必要给你讲一讲这个模式。除此之外,为了最大化学习效果,我今天不只是单纯地讲解原理和实现,更重要的是,我会手把手带你还原访问者模式诞生的思维过程,让你切身感受到创造一种新的设计模式出来并不是件难事。
话不多说,让我们正式开始今天的学习吧!

带你“发明”访问者模式

假设我们从网站上爬取了很多资源文件,它们的格式有三种:PDF、PPT、Word。我们现在要开发一个工具来处理这批资源文件。这个工具的其中一个功能是,把这些资源文件中的文本内容抽取出来放到 txt 文件中。如果让你来实现,你会怎么来做呢?
实现这个功能并不难,不同的人有不同的写法,我将其中一种代码实现方式贴在这里。其中,ResourceFile 是一个抽象类,包含一个抽象函数 extract2txt()。PdfFile、PPTFile、WordFile 都继承 ResourceFile 类,并且重写了 extract2txt() 函数。在 ToolApplication 中,我们可以利用多态特性,根据对象的实际类型,来决定执行哪个方法。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入讲解了访问者模式的设计思想和实现方式,旨在帮助读者快速了解访问者模式的概念和应用。通过手把手地带领读者还原访问者模式诞生的思维过程,让读者切身感受到创造一种新的设计模式并不是难事。文章通过代码示例展示了访问者模式的演进思路,重点讲解了如何利用访问者模式解决代码扩展性和灵活性的问题。总的来说,本文通过深入的讲解和实际代码演示,帮助读者理解访问者模式的设计思想和实现方式,为读者提供了一种快速理解和应用访问者模式的方法。同时,作者也提到了访问者模式的应用场景和难点,以及建议在项目中谨慎使用这种模式。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《设计模式之美》
新⼈⾸单¥98
立即购买
登录 后留言

全部留言(94)

  • 最新
  • 精选
  • DFighting
    看完这个访问者模式,我第一反应是和桥接模式好相似,都是好像是操作与数据独立扩展,但是桥接模式主要使用的是组合,他的数据结构不支持扩展,进而我就理解了访问者模式和桥接模式不同的地方:桥接是对固定的数据结合进行多维度的独立扩展,每个维度的扩展可以在使用的时候随意组合,但是数据结构不支持改变,因为单纯的组合模式实现不了这种静态的“多态”;但是访问者模式最关键的地方是vist(this),这么实现就做到了数据和操作两个的独立扩展,有新增的数据类型或者操作的时候都只需要按需扩展数据结构和操作即可,虽然这会涉及到所有的操作类,但是这并未对已存在的功能做出影响,是符合开闭原则的,但是这个扩展操作因为需要支持多种数据结构,所以不适合如桥接模式这种多维度独立扩展,因为那样需要改动很多的类和代码,不合适

    作者回复: 嗯嗯

    2020-11-22
    4
    9
  • 起个名字好难
    把属性和行为分离的前提是抽象,如果要访问的对象结构一旦改变必然是灾难。在我看来最困难的地方还是抽象。我在阅读jsqlparser源码的时候发现代码相对容易理解,但是在抽象sql时,真的感觉类都要爆炸了,没有一定的抽象能力真的搞不定

    作者回复: 嗯嗯 加油

    2020-11-29
  • CoderArthur
    很nice的设计模式,差点略过没看,还好点进来再看第二遍,第二遍略懂了点。 昨天在实现迭代器模式的时候,也碰到过运行时期动态选择和编译器静态选择的问题,现在按照作者的写法思考下怎么解决我的问题。

    作者回复: 是有点不好理解

    2020-11-22
  • test
    访问者模式解决的痛点主要是需要动态绑定的类型,所以调用哪个重载版本,其参数中的子类必须传入静态类型为目标子类的参数,并在方法中使用传入参数的动态绑定。如果不使用访问者模式,可以使用策略模式,使用工厂模式在map中保存type和具体子类实例的映射,在使用的时候,根据type的不同调用不同子类的方法(动态绑定)。
    2020-04-08
    4
    65
  • Jxin
    1.虽然策略模式也能实现,但这个场景用访问者模式其实会优雅很多。 2.因为多种类型的同个操作聚合在了一起,那么因为这些类型是同父类的,所以属于父类的一些相同操作就能抽私有共用方法。 3.而策略模式,因为各个类型的代码都分割开了,那么就只好复制黏贴公共部分了。 4.另外,写合情合理的优雅代码,然后别人看不懂,一顿吹也是极爽的。只是一般节奏都挺快,第一时间可能就是策略模式走你,然后就没有然后了。
    2020-04-08
    8
    41
  • 李小四
    设计模式_68: # 作业: 今天的需求,我的第一反映是策略模式。 # 感想: 挺认同文章的观点,别人写了这种模式要看得懂,自己还是不要用比较好。 给转述师提个Tip: 程序开发中常常用数字`2`代替`to`、用数字`4`代替`for`,比如文中的`extract2txt`,这时要读作`extract to txt`,而不是`extract 2(中文读音er) txt`。
    2020-04-08
    12
    19
  • Liam
    antlr(编译器框架)对语法树进行解析的时候就是通过visitor模式实现了扩展
    2020-04-08
    1
    15
  • 小晏子
    课后思考:可以使用策略模式,对于不同的处理方式定义不同的接口,然后接口中提供对于不同类型文件的实现,再使用静态工厂类保存不同文件类型和不同处理方法的映射关系。对于后续扩展的新增文件处理方法,比如composer,按同样的方式实现一组策略,然后修改application代码使用对应的策略。
    2020-04-08
    1
    12
  • 写代码的
    当一组重载函数的参数类型是继承同一个接口或者父类的话,如果传入的参数的静态类型是这个接口或者父类,java是无法决定使用哪个重载函数的。访问者模式的巧妙之处在于,我们可以借助多态,利用多态的动态分派特性,让这个参数暴露一个方法,使得这一组重载函数(或者说声明这组重载函数的类)能进入参数对象内部(也就是让参数暴露一个参数类型是这组重载函数所在类的方法)。一旦这组重载函数进入了参数对象内部,这组重载函数就知道了它的真实类型了,这个时候再调用重载函数,就能根据参数的具体类型找到合适的重载方法了。 用一个通俗的例子来理解。敌军有多种类型的基地,我军有一套破坏敌军各种类型基地的方案,但是敌军的基地经过伪装,看上去都一样,我军在外面是无法决定使用哪套方案来攻击基地的。幸运的是,我军了解到,敌军每天中午会允许一批物资车进入基地,于是我方军队伪装成了敌军物资车进入到了敌军内部。进入基地之后我军了解了其真实类型,于是我军根据基地真实类型选择了相应的攻击方案,将敌军基地摧毁。
    2020-09-13
    1
    10
  • Frank
    如果不使用访问者模式,也许这也是一种改造方法:在Extractor类种在定义一个重载的方法,形参的类型为:ResourceFile,在该方法种判断参数的实际类型后再做分派。如下所示
    2020-04-11
    5
    8
收起评论
显示
设置
留言
94
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部