69 | 访问者模式(下):为什么支持双分派的语言不需要访问者模式?
王争
该思维导图由 AI 生成,仅供参考
上一节课中,我们学习了访问者模式的原理和实现,并且还原了访问者模式诞生的思维过程。总体上来讲,这个模式的代码实现比较难,所以应用场景并不多。从应用开发的角度来说,它的确不是我们学习的重点。
不过,我们前面反复说过,学习我的专栏,并不只是让你掌握知识,更重要的是锻炼你分析、解决问题的能力,锻炼你的逻辑思维能力,所以,今天我们继续把访问者模式作为引子,一块讨论一下这样两个问题,希望能激发你的深度思考:
为什么支持双分派的语言不需要访问者模式呢?
除了访问者模式,上一节课中的例子还有其他实现方案吗?
话不多说,让我们正式开始今天的学习吧!
为什么支持双分派的语言不需要访问者模式?
实际上,讲到访问者模式,大部分书籍或者资料都会讲到 Double Dispatch,中文翻译为双分派。虽然学习访问者模式,并不用非得理解这个概念,我们前面的讲解就没有提到它,但是,为了让你在查看其它书籍或者资料的时候,不会卡在这个概念上,我觉得有必要在这里讲一下。
除此之外,我觉得,学习 Double Dispatch 还能加深你对访问者模式的理解,而且能一并帮你搞清楚今天文章标题中的这个问题:为什么支持双分派的语言就不需要访问者模式?这个问题在面试中可是会被问到的哦!
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
- 深入了解
- 翻译
- 解释
- 总结
本文讨论了支持双分派的语言为何不需要访问者模式。首先介绍了Single Dispatch和Double Dispatch的概念,并以Java语言为例说明了Java只支持Single Dispatch的特性。接着通过代码示例展示了如果Java语言支持Double Dispatch,就不需要访问者模式的情况。文章还提到了其他实现方案,如工厂模式,来处理资源文件,以及访问者模式和双分派的应用。总体上,文章强调了访问者模式难以理解,应用场景有限,不建议在项目中使用。此外,重点讲解了Double Dispatch的概念,以及在面向对象编程语言中的应用。文章通过清晰的概念解释和具体的代码示例,帮助读者理解了访问者模式和双分派的概念,以及它们在面向对象编程语言中的应用。
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《设计模式之美》,新⼈⾸单¥98
《设计模式之美》,新⼈⾸单¥98
立即购买
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
登录 后留言
全部留言(51)
- 最新
- 精选
- DFighting在面向对象的世界里,万物都可以作为对象,操作如果不需要扩展,那么应该聚合在对象之中,但是如果操作需要扩展,那么这个就可以单独拿出来,代码设计中的面向对象不是一定需要和现实世界的理解的对象一致的,程序未来的演进方向在何处,面向对象就该如何抽象;至于问题,被调用对象的这一个多态在Single Dispatch中也是支持的
作者回复: 嗯嗯 ������
2020-11-227 - Laughing1. 个人认为并不违反面向对象的设计原则,基于单分派语言的特性,访问者模式本质上是解决了没有指定对象实际类型时函数重载的问题,这是语言特性导致的,开闭原则指的是”对修改关闭,对扩展开放“,理论上该模式并没有实质上的修改操作,更多的是通过另一种形式完成了功能扩展。 2. I am ChildClass's f() 输出两次,因为运行时对象的实际类型是child,所以会执行child的f函数
作者回复: 嗯嗯 ������
2020-11-262 - 大悟感觉访问者模式的核心不在于双重分派,而是在于将数据对象和操作分离的思想。双重分派只是由于 Java 只支持单分派而采取的手段,不是目的。上文中说理解双重分派是理解访问者模式的核心,实在不太认同,感觉有点舍本逐末了。
作者回复: 所有的行为模式都是将行为分离,这点不是访问者模式独有的。如果支持double dispatch,完全没必要有访问者模式。
2020-08-301 - 小晏子课后思考: 1. 看要怎么理解这个问题了,简单来看将操作与对象分离是违背了面向对象的设计原则,但从另外的角度看,将操作也看做对象,然后将不同的对象进行组合,那么并不违背面向对象的设计,而且在访问者模式中,操作可能不断增加,是属于是变化比较多的,将不变的部分和可变的部分开,然后使用组合的方式使用是符合面向对象设计的。 2. 会输出: I am ChildClass's f(). I am ChildClass's f(). 调用demo.overloadFunction(p);时,会根据重载特性调用函数 public void overloadFunction(ParentClass p) { p.f(); } 运行时,因为p是ChildClass对象,所以会根据多态特性使用ChildClass的f函数。2020-04-103104
- ,关于访问者模式的替代方式,我的看法: 先放总结: 行为不可抽象+水平扩展较多-->工厂模式更合适 行为可抽象+垂直扩展较多-->模板方法模式更合适 我认为模板方法模式和工厂模式都可以,具体使用哪种,应该根据扩展的方向来确认: 当前的场景是对不同文件格式的文本进行处理,目前有word,ppt与pdf三种格式,他们的行为都不一致,比如word的抽取,分析与pdf的抽取,分析行为不一致,而且扩展的方向是添加不同的文件格式,比如txt格式,excel等格式,那么最好的方式就是采用工厂模式,每次添加新格式需要添加新的工厂,实现相应的方法 如果扩展的方向是给不同文件格式添加更多的功能,同时这些行为可以抽象出来,比如当前有抽取,分析,压缩等功能,他们有很大一部分可以抽象到父类,那么我要给所有的文件格式添加敏感词替换,格式化文本等功能,就可以将它们添加到父类,而不用每个工厂都加一遍,这种情况模板方法模式更合适2020-04-10531
- 迷羊1.争哥在前面讲面向对象的设计原则时就已经解答了这个问题,不要太死板的遵守各种设置原则,定义,只要写出来的代码是可扩展、可读性、可复用的代码就是好代码。 2.代码执行结果 I am ChildClass's f(). I am ChildClass's f(). 虽然执行重载方法时是根据参数的编译时类型,但是调用哪个对象的方法是根据对象的运行时类型来决定的,所以最终调用的还是实际类型的f()方法。2020-04-1027
- 寒光老师举的例子不太好,访问者模式更多是用来在一组对象中收集信息,然后汇总。 由于不同对象提供的信息不一样,所以才会有多分派,因而访问者会提供不同的访问方法,这些方法应该有更可读的名字,而不应该是统一的visit重载方法。2022-06-085
- zj实际上操作与对象并没有分开吧,访问者accpect方法其实就是操作了,只不过将操作部分抽象出来了,组合到对象里而已2020-04-1325
- Frank打卡 今日学习访问者模式下,收获如下: 访问者模式实现比较难于理解,主要要理解静态分派和动态分派。通过本专栏的内容学习到了双分派和单分派。自己使用的主要语言Java是单分派。单分派就是指的是执行哪个对象的方法,根据对象的运行时类型来决定;执行对象的哪个方法,根据方法参数的编译时类型来决定。 理解分派之前需要理解变量是有静态类型和实际类型的,如 A a = new B(), 变量a的静态类型(声明类型)是A,实际类型是B。如果是 A a = new A(),那么变量a的静态类型和实际类型都是A。方法调用过程中判断是用父类对象还是子类对象其实就是多态,运行时根据变量的实际类型来决定是使用子类对象中的方法还是父类对象中的方法。其中涉及到invokevirtaul字节码多态查找流程,简单的理解就是先在已确定对象中寻找方法(如子类),如果找不到往父类中找,如果一直找不到就抛出异常。在确定调用对象后(如确定是子类对象)在调用方法时可能存在方法的重载,这时候就涉及到静态分派(静态绑定),根据变量的静态类型(声明类型)来判断方法的调用版本。 课后思考: 1. 访问者模式将操作与对象分离,是否违背面向对象设计原则?你怎么看待这个问题呢?对于这个问题,我觉得不能死套设计原则,对于业务场景,要有所取舍。就像专栏中的这个例子,如果所有的功能都写在了相关类中,随着需求不断的迭代,类会变膨胀,可维护性、可读性、可测试性都会变差,后期维护成本会变高。如果一开始就能确定需求不会变化,就只有这么两类操作,那么可以不用访问者这么模式,直接写在相关类中。 2. DemoMain 的输出结果会是什么呢? ChildClass 类中的f()方法会被调用两次。首先根据方法重载是静态绑定,会调用形参是ParentClass的overloadFunction方法,在该方法中“p.f();” 变量“p”的静态类型是 ParentClass,而实际类型却是ChildClass。根据多态的动态绑定,在ChildClass类中复写了父类中的f()方法,因此,这里会调用ChildClass中的f();2020-04-114
- 88591访问者模式将操作与对象分离,是否违背面向对象设计原则 ?我理解是没有违背,面向对象里面的封装应该是针对 属性与操作都是比较明确的情况下。访问者模式模式中的操作实际是不确定,不稳定的。所以将这部分与对象分离出来了。也就是分离了对象中稳定的(抽象出共性)与非稳定性的部分。因为我们不确定这个对象后续还要添加什么操作,那么我们就定义一个操作,可以操作对象的数据(面向对象中的抽象)。2020-04-143
收起评论