让我最后罗嗦一下Visitor模式——关于双重分派
2005-08-20 13:21 FantasySoft 阅读(4609) 评论(8) 编辑 收藏 举报 昨天罗嗦了一下Visitor模式,idior以及Cavingdeep兄都写下了很多促使我进一步去思考的Feedback,在这里谢谢两位了!感谢的话说完了,让我们言归正传——开始罗嗦,呵呵。
在昨天的Post中我提到了Visitor模式中最重要的一点是:double-dispatch(双重分派)。说来惭愧,昨天在写post的时候,我仅仅在震宇兄的Post中对double-dispatch有了概念上的理解,并且认为accept方法是为了实现double-dispatch的。然而idior和Cavingdeep兄的问题让我心里没了谱:accept为什么要存在,为什么不直接visit呢?早上在看了Cavingdeep兄的Post之后,我想要解决这个问题,得从到底什么是双重分派入手了。于是google了一番,终于有点开窍了。
在说双重分派以前,我们先来看看以下代码[1]:




我们都知道由于动态类型绑定,尽管a声明为SuperClass类型,但a.commonMethod调用的是SubA中override的commonMethod,这就是多态的一种体现。如果在SubA类中有这样的重载方法:






那么a.commonMethod调用的又是哪个方法呢?你的心里有非常肯定的答案吗?写段代码尝试一下,你会发现第一个commonMethod被调用了。这里就引出了一个问题:动态类型绑定只会体现在方法的调用者身上,而方法的参数类型则会在编译期由编译器决定。
如果参数类型也能够在运行期决定,那么哪个commonMethod被调用就由方法调用者和方法参数共同在运行期决定了。那么如何实现参数类型在运行期绑定呢?既然方法调用者的类型是运行期才确定的,那么我们就可以反客为主了,将方法参数变成方法调用者。



至此,您应该明白双重分派的涵义了吧?哪个commonMethod最终被调用经过两次运行期类型绑定才确定下来,这样的过程就是双重分派了。由此延伸开来,多重分派也就不难理解了。
在双重分派当中,还有一个关键细节,您是否发现了呢?那就是您所期待的SubA中的CommonMethod(SubB arg)方法被调用的情况并没有出现,反而是SubB中的CommonMethod(SubA arg)的方法被调用了。不管怎么样,双重分派给程序带来了更多的灵活性。
说完了双重分派,我们再回到Visitor模式上来。Visitor模式其实就是将以上所说的commonMethod从数据对象中抽离出来封装至单独的类中,那么为了维持原有的双重分派,accept方法就派上了用场。没有了accept,就无法在数据操作从数据对象中分离出来的同时保持双重分派。同时,您也会发现commonMethod有一个非常大的特点:它是一个跨对象操作的方法。对应到Visitor模式里众多的Visitor类,重载的visit方法正好体现了跨对象操作的特点。
也许我讲述的还不是很清楚,有兴趣的朋友可以参考以下这篇给我带来很大启发的文章。
[1] Double Dispatch Mechanism
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述