您的位置:首页 > 编程语言 > C语言/C++

Modern C++ Design 笔记 第十章 Visitor

2009-01-31 00:26 323 查看
从开始学习设计模式的时候,就有一个概念,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的模板实现完全参照了这样的想法.里面的亮点就在于这个函数实现:

// Visitable part
template <typename R = void>
class BaseVisitable
{
public:
typedef R ReturnType;
virtual ~BaseVisitable() {}
virtual R Accept(BaseVisitor&) = 0;
protected:
template <class T>
static ReturnType AcceptImpl(T& visited, BaseVisitor& guest)
{
// Apply the Acyclic Visitor
if (Visitor<T>* p =
dynamic_cast<Visitor<T>*>(&guest))
{
return p->Visit(visited);
}
return ReturnType();
}
};
#define DEFINE_VISITABLE() /
virtual ReturnType Accept(BaseVisitor& guest) /
{ return AcceptImpl(*this, guest); }


这个protected函数只需要派生类都有DEFINE_VISITABLE()的宏定义就可以直接调用.

另一点值得注意的就是这个最后一句return ReturnType(); 这个在文中有些CatchAll模板的策略我们就不展开了,为一个不喜欢的且不是很成功模式说了这么多似乎有点多了:)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: