访问者模式简介
在项目开发中,有时会有复杂操作的结构对象,不同的访问者会对此对象做出不同的操作,访问者模式就是为了解决此种问题。
访问者模式有访问者和被访问元素两个角色。被访问元素往往结构复杂,对于不同的访问者,会有不同的访问方式,且被访问元素通常不是单一存在的,而是以集合的形式存在一个对象结构中,访问者可以通过遍历的方式对其一一访问。
访问者模式:表示一个作用于某对象结构中的各个元素的操作。访问者模式让用户可以在不改变各元素的前提下定义作用于这些元素的新操作。
访问者模式结构
访问者模式结构:
- Visitor(抽象访问者):抽象类,声明了访问对象结构中不同具体元素的方法,由方法名称可知该方法将访问对象结构中的某个具体元素;
- ConcreteVisitor(具体访问者):访问某个具体元素的访问者,实现具体的访问方法;
- Element(抽象元素):抽象类,一般声明一个accept()的方法,用于接受访问者的访问,accept()方法常常以一个抽象访问者的指针作为参数;
- ConcreteElement(具体元素):针对具体被访问的元素,实现accept()方法;
- ObjectStructure(对象结构):元素的集合,提供了遍历对象结构中所有元素的方法。对象结构存储了不同类型的元素对象,以供不同的访问者访问。
访问者模式代码实例
#include <iostream>
#include <vector>
using namespace std;
class ConcreteElementA;
class ConcreteElementB;
class Visitor
{
public:
virtual void VisitConcreteElementA(ConcreteElementA *pElementA) = 0;
virtual void VisitConcreteElementB(ConcreteElementB *pElementB) = 0;
};
class ConcreteVisitor1 : public Visitor
{
public:
void VisitConcreteElementA(ConcreteElementA *pElementA);
void VisitConcreteElementB(ConcreteElementB *pElementB);
};
void ConcreteVisitor1::VisitConcreteElementA(ConcreteElementA *pElementA)
{
// 现在根据传进来的pElementA,可以对ConcreteElementA中的element进行操作
}
void ConcreteVisitor1::VisitConcreteElementB(ConcreteElementB *pElementB)
{
// 现在根据传进来的pElementB,可以对ConcreteElementB中的element进行操作
}
class ConcreteVisitor2 : public Visitor
{
public:
void VisitConcreteElementA(ConcreteElementA *pElementA);
void VisitConcreteElementB(ConcreteElementB *pElementB);
};
void ConcreteVisitor2::VisitConcreteElementA(ConcreteElementA *pElementA)
{
// ...
}
void ConcreteVisitor2::VisitConcreteElementB(ConcreteElementB *pElementB)
{
// ...
}
// Element object
class Element
{
public:
virtual void Accept(Visitor *pVisitor) = 0;
};
class ConcreteElementA : public Element
{
public:
void Accept(Visitor *pVisitor);
};
void ConcreteElementA::Accept(Visitor *pVisitor)
{
pVisitor->VisitConcreteElementA(this);
}
class ConcreteElementB : public Element
{
public:
void Accept(Visitor *pVisitor);
};
void ConcreteElementB::Accept(Visitor *pVisitor)
{
pVisitor->VisitConcreteElementB(this);
}
// ObjectStructure类,能枚举它的元素,可以提供一个高层的接口以允许访问者访问它的元素
class ObjectStructure
{
public:
void Attach(Element *pElement);
void Detach(Element *pElement);
void Accept(Visitor *pVisitor);
private:
vector<Element *> elements;
};
void ObjectStructure::Attach(Element *pElement)
{
elements.push_back(pElement);
}
void ObjectStructure::Detach(Element *pElement)
{
auto i = elements.begin();
for ( ; i != elements.end(); i ++)
{
if(*i == pElement) {
break;
}
}
if (i != elements.end())
{
elements.erase(i);
}
}
void ObjectStructure::Accept(Visitor *pVisitor)
{
// 为每一个element设置visitor,进行对应的操作
for (vector<Element *>::const_iterator it = elements.begin(); it != elements.end(); ++it)
{
(*it)->Accept(pVisitor);
}
}
int main()
{
ObjectStructure *pObject = new ObjectStructure;
ConcreteElementA *pElementA = new ConcreteElementA;
ConcreteElementB *pElementB = new ConcreteElementB;
pObject->Attach(pElementA);
pObject->Attach(pElementB);
ConcreteVisitor1 *pVisitor1 = new ConcreteVisitor1;
ConcreteVisitor2 *pVisitor2 = new ConcreteVisitor2;
pObject->Accept(pVisitor1);
pObject->Accept(pVisitor2);
if (pVisitor2)
delete pVisitor2;
if (pVisitor1)
delete pVisitor1;
if (pElementB)
delete pElementB;
if (pElementA)
delete pElementA;
if (pObject)
delete pObject;
return 0;
}
访问者模式总结
访问者模式的结构相对较复杂,在实际应用中使用频率较低。如果系统中存在一个复杂的对象结构,且不同的访问者对其具有不同的操作,那么可以考虑使用访问者模式。访问者模式的特点总结如下:
优点:
- 增加新的访问者很方便,即增加一个新的具体访问者类,定义新的访问方式,无需修改原有代码,符合开闭原则;
- 被访问元素集中在一个对象结构中,类的职责更清晰,利于对象结构中元素对象的复用;
缺点:
- 增加新的元素类很困难,增加新的元素时,在抽象访问者类中需要增加一个对新增的元素方法的声明,即要修改抽象访问者代码;此外还要增加新的具体访问者以实现对新增元素的访问,不符合开闭原则;
- 破坏了对象的封装性,访问者模式要求访问者对象访问并调用每一个元素对象的操作,那么元素对象必须暴露自己的内部操作和状态,否则访问者无法访问。