从开始学习设计模式的时候,就有一个概念,visitor是一个鸡肋模式.由于visitor模式对于类结构稳定性的要求非常高,所以其实自己感觉在现实生活中的运用很少.(在现实中一般总可以,或者总趋向于添加一个虚函数接口了事).
当然visitor不止现在这个问题,另一个突出的问题就是循环依赖.这对于C++来说,或者说对于OO来说都是一个尴尬的问题,在<<C++ coding standard>>中也提及了"第22条 尽量减少定义性依赖。避免循环依赖 "而其中的例子恰恰就是这个visitor.
现在让我们来揭开这块visitor的遮羞布,最开始,我们先看看传统的visitor的UML图.
我们可以清楚的从这个图上看到这几点依赖:
1. DocElement作为元素的基类依赖于DocElementVisitor的基类
2. DocElementVisitor依赖于DocElement的所有派生类包括Paragraph和RasterBitmap
3. 所有的DocElement的派生类当然依赖与DocElement类!
就这样一个环出现了!!!所以在这里直观的想法就是这样一个模式有点不make sense. 就是基于这样的一个考虑,在这一章里面,作者非常乐意分享一下他的Acyclic visitor.注意这个Acyclic的意思就是不循环的.
当然在展开书中的实现之前,先看看普通的是实现是怎么样的. Robert C. Martin帮助我们找到了症结,给出了解决方案. 其实我们直观的想法就是如果这个Visitor可以不包含所有的visit(ELement& ele)(派生类)的接口,或者说在只在Visitor这个类里面不出现就非常好了! 而这里的解决方案也确实用到了这样的想法.
这里的方案就是DocStatus不从DocElementVisitor继承下来,而是同时也从RasterBitmapVisitor和ParagraphVisitor继承下来,那就是说, 也就是说在Paragraph在accept的时候,把visitor&转化成了ParagraphVisitor,当然你在ParagraphVisitor里面可以很从容的让他访问DocStats的VisitParagraph接口实现(因为在ParagraphVisitor这个函数是纯虚函数).这样兜了一个大圈子找到了DocStats的对应函数.至少现在的结构消除了循环的依赖,可以看到的是DocElementVisitor不再需要任何一个具体的Visitorxxx的接口,而只是一个纯虚析构函数的申明.
之后给出的一个acyclic visitor的模板实现完全参照了这样的想法.里面的亮点就在于这个函数实现:
这个protected函数只需要派生类都有DEFINE_VISITABLE()的宏定义就可以直接调用.
另一点值得注意的就是这个最后一句return ReturnType(); 这个在文中有些CatchAll模板的策略我们就不展开了,为一个不喜欢的且不是很成功模式说了这么多似乎有点多了:)