我本来想写成一篇文章的,一来没时间,二来文章缺乏交互性,而且也觉得有建立一个独立的设计模式栏目的必要,就让它作为第一个帖子吧。
我们在编写一个新的程序的时候,总会遇到需要对已有的类进行扩展的情况。譬如说,如果我们有一个类体系,Shape是所有图形的基类;他有两个子类:Rectangle和Circle,分别表示正方形和圆形。Shape包括两个函数:Draw和Move。当然,作为一个图形基类,还应该包括更多的功能,但是我们知道,我们现在无论如何都不可能预知所有的功能。也就是说,我们必须为未来的扩展作准备。大家都知道,在现有的类层次结构比较稳定的话,可以使用visitor这个设计模式来在类层次以外扩展功能。
visitor的核心是一个基类和一个虚函数。也就是:
声明一个独立于类体系的类Visit,为类体系中的每一个类加入一个纯虚函数,如:virtual void Visit(Rectangle&); virtual void Visit(Circle&);
在类层次的每个类中加入一个虚函数:virtual void Accept(Visit& v); 其实现很简单:v.Visit(*this);
visitor是通过什么机制来使得每个类能够调用相应的函数的呢?
譬如说我要实现一个功能:把一个列表中的每个Shape移动到一个新的位置(根据不同的形状有不同的计算方法),然后重画它。我可以从Visit派生一个新的类:SpecialMoveShape,它的功能为:
代码:
class SpecialMoveShape:public Visitor
{
virtual void Visit(Rectangle& rect)
{
// 先根据这个Rectangle计算移动多少
// 因为我们知道这是一个Rectangle,我们可以调用Rectangle独有的函数。譬如getSize,...
rect.Move();
rect.Draw();
}
// 其余函数实现略过...
};
假设p是一个Shape&,但是实际上指向一个Rectangle,我们声明一个SpecialMoveShape的对象v,然后调用p.Accept(v)。由于Accept是个虚函数,最终将调用到Rectangle::Accept,然后调用v.Visit(*this);因为this的类型是Rectangle*,而v正好有一个函数Visit(Rectangle&),所以最终它将被调用,实现了针对Rectangle进行移动并重画的功能。其实,我们声明的这个类SpecialMoveShape等于在Shape类中加入了一个SpecialMove纯虚函数。
顺便说一句,visitor把本来应该在类体系的内部实现的功能转移到类体系的外部来实现,其实它和很多其它的模式一样,体现了一种理念:如果程序要完成一个功能,那么它必然在这个程序的某个地方被实现。或是在常见的地方,或是在不常见的地方。我们学习很多设计模式的目的就是能够自如的把我们想要实现的功能实现在合适的地方。
下面是示例代码,其中加注释的部分是加入一个新的类:Triangle以后做的改动。
visitor.h
代码:
#ifndef __A0CC449E0FC0993D677D76384ECE93051C2525CA76E1FAE__
#define __A0CC449E0FC0993D677D76384ECE93051C2525CA76E1FAE__
#include <iostream>
namespace Visitor
{
struct ShapeVisitor;
class Rectangle;
class Circle;
/*********************************************************************************/
// code added for triangle
class Triangle;
/*********************************************************************************/
struct Shape
{
virtual void Draw()=0;
virtual void Move()=0;
virtual void Accept(ShapeVisitor*)=0;
virtual ~Shape()=0{};
};
struct ShapeVisitor
{
virtual void Visit(Rectangle*)=0;
virtual void Visit(Circle*)=0;
/*********************************************************************************/
// code added for triangle
virtual void Visit(Triangle*)=0;
/*********************************************************************************/
virtual ~ShapeVisitor()=0{};
};
class Rectangle:public Shape
{
public:
virtual void Draw()
{
std::cerr<<"Rectangle Draw"<<std::endl;
}
virtual void Move()
{
std::cerr<<"Rectangle Move"<<std::endl;
}
virtual void Accept(ShapeVisitor* visitor)
{
visitor->Visit(this);
}
};
class Circle:public Shape
{
public:
virtual void Draw()
{
std::cerr<<"Circle Draw"<<std::endl;
}
virtual void Move()
{
std::cerr<<"Circle Move"<<std::endl;
}
virtual void Accept(ShapeVisitor* visitor)
{
visitor->Visit(this);
}
};
/*********************************************************************************/
// code added for triangle
class Triangle:public Shape
{
public:
virtual void Draw()
{
std::cerr<<"Triangle Draw"<<std::endl;
}
virtual void Move()
{
std::cerr<<"Triangle Move"<<std::endl;
}
virtual void Accept(ShapeVisitor* visitor)
{
visitor->Visit(this);
}
};
/*********************************************************************************/
}
#endif //__A0CC449E0FC0993D677D76384ECE93051C2525CA76E1FAE__
VisitorTest.cpp
代码:
#include "Visitor.h"
#include <iostream>
namespace Visitor
{
class MyVisitor:public ShapeVisitor
{
public:
virtual void Visit(Rectangle* r)
{
r->Move();
r->Draw();
}
virtual void Visit(Circle* c)
{
c->Move();
c->Draw();
}
/*********************************************************************************/
// code added for triangle
virtual void Visit(Triangle* t)
{
t->Move();
t->Draw();
}
/*********************************************************************************/
};
void test()
{
Rectangle rect;
Circle circle;
MyVisitor visitor;
rect.Accept(&visitor);
circle.Accept(&visitor);
/*********************************************************************************/
// code added for triangle
Triangle triangle;
triangle.Accept(&visitor);
/*********************************************************************************/
}
}
int main(int argc, char* argv[])
{
std::cerr<<"------------------------------------------------------------\nTesting Visitor"<<std::endl;
Visitor::test();
return 0;
}