访问者模式是一种分离对象数据和行为的方法,通过这种分离,可以为一个已存在的类或类群增加新的操作而不用更改他们的数据。
这个模式其实和迭代器模式在很大程度上有相似的东西,因为迭代器也是访问对象而不改变对象的元素,但是迭代器的使用范围更加单一也更具有针对性。而访问者模式能够增加对对象的操作而不修改其数据。
为什么要使用访问者模式?
如何扩展一个现有的类层次结构来实现新行为?一般的方法是给类添加新的方法。但是万一新行为和现有对象模型不兼容怎么办?还有,类层次结构设计人员可能无法预知以后开发过程中将会需要哪些功能。以及,如果已有的类层次结构不允许修改代码,怎么能扩展行为呢? 这个时候,访问者就能够起到作用。
访问者模式中的角色
访问者抽象接口,通过visit(Element)方法访问Element(数据结构),完成对Element的操作行为。
具体访问者(ConcreteVisitor)访问者的具体实现类。
元素(Element),也就是被访问者通过accept(Visitor)方法接受Visitor的访问。具体元素(ConcreteElement) 元素的具体实现类。
对象结构(ObjectStructure) 拥有一组元素的组合对象。ObjectStructure本身也可以作为被访问者。
访问者模式的结构图如下:
使用访问者模式的条件
1))需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而你想避免让这些操作“污染”这些对象的类。Visitor使得你可以将相关的操作集中起来定义在一个类中。
2) 当该对象结构被很多应用共享时,用Visitor模式让每个应用仅包含需要用到的操作。
3) 定义对象结构的类很少改变,但经常需要在此结构上定义新的操作。改变对象结构类需要重定义对所有访问者的接口,这可能需要很大的代价。如果对象结构类经常改变,那么可能还是在这些类中定义这些操作较好。
从上述所讲的条件,我们知道了访问者模式其实用在最多的地方应该是扩展接口的时候。同时,我们不用每次增加操作都定义在对象的结构内部,以免造成类结构过于臃肿。
举一个实际的使用例子吧。
//元素(被访问者)
class CPerson //纯虚类
{
protected:
stringname; //或者改成数组
intgender;
CPerson()
public:
virtual void Accept( CVisitor& ) //=0;//纯虚函数
//访问者
class CVisitor
{
public:
virtual void Visit( CStudent& ) = 0;
virtual void Visit( CTeacher& ) = 0;
};
//具体元素
class CStudent: public CPerson
{
private:
intgrade; //年级
public:
CStudent(stringName,int Gender,int Grade)
{
name=Name;
gender=Gender;
grade=Grade;
}
public:
virtual void Accept(CVisitor& printer) //虚函数
voidSetGrade(int Grade)
intGetGrade() const
};
//具体元素
class CTeacher:public CPerson
{
private:
intservice_time;//工龄
public:
CTeacher(stringName,int Grade, int ServiceTime)
{
name= Name;
gender= Grade;
service_time= ServiceTime;
}
public:
virtualvoid Accept(CVisitor& printer)
voidSetServiceTime(int ServiceTime)
intGetServiceTime() const
};
在上述代码中,从程序的标记处可以看得出来,访问者模式是通过统一管理对象集合中的各个类的访问,而在每个类中,均提供对象的接口一遍访问者来访问。因此,当我们每次增加一个类的操作的时候,只需要为访问者提供一个可以操作的接口,同事在访问者中提供一个访问接口,访问者通过访问接口,以输入参数的形式来获得对对象中的各个类的操作,而不用修改到其中的具体元素。
当然,有时候使用访问者模式未必是能够增加工作效率,如果对象中的这个操作很频繁使用的话,那么最好还是封装在对象的内部中,而且如果要完整的访问对象中的元素,很可能会涉及到暴漏处对象的成员属性,对于数据封装性和安全性并不是很好的方式。