一,简介
访问者模式(visitor),表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
二,角色
visitor(访问者)
为对象结构中每一个ConcreteElement声明一个visit操作
ConcreteVisitor(具体访问者)
实现每个由visitor声明的操作
Element(元素)
定义一个accept操作,它通常以一个vistor作为参数
ConcreteElement(具体元素)
实现accept操作,通过调用visitor的visit方法来实现对元素的访问
ObjectStructure(对象结构)
能够枚举它的元素,同时提供一个高层接口,以允许访问者访问它的元素。
三,举例
class BellTower;
class TerracottaWarriors;
//访问者 为每个景点提供一个访问方法
class IVisitor {
public:
virtual ~IVisitor(){}
virtual void Visit(BellTower*) = 0;
virtual void Visit(TerracottaWarriors*) = 0;
};
//具体访问者:游客
class Tourist :public IVisitor {
public:
virtual void Visit(BellTower*) {
cout << "参观 Bell Tower" << endl;
}
virtual void Visit(TerracottaWarriors*) {
cout << "参观 Terracotta Warriors" << endl;
}
};
class Cleaner :public IVisitor {
public:
virtual void Visit(BellTower*) {
cout << "清洁 Bell Tower" << endl;
}
virtual void Visit(TerracottaWarriors*) {
cout << "清洁 Terracotta Warriors" << endl;
}
};
//景点中 定义一个accept接口,用于接收访问者的访问
class IPlace {
public:
virtual ~IPlace() {}
virtual void Accept(IVisitor* visitor) = 0;
};
//具体元素
class BellTower :public IPlace {
public:
virtual void Accept(IVisitor *visitor) {
cout << "Bell Tower 接收访问" << endl;
visitor->Visit(this);
}
};
class TerracottaWarriors : public IPlace
{
public:
virtual void Accept(IVisitor *visitor) override {
std::cout << "Terracotta Warriors is accepting visitor." << std::endl;
visitor->Visit(this);
}
};
// 城市(西安)
class City
{
public:
void Attach(IPlace *place) {
m_places.push_back(place);
}
void Detach(IPlace *place) {
m_places.remove(place);
}
void Accept(IVisitor *visitor) {
// 为每一个 element 设置 visitor,进行对应的操作
for (std::list<IPlace*>::iterator it = m_places.begin(); it != m_places.end(); ++it) {
(*it)->Accept(visitor);
}
}
private:
std::list<IPlace *> m_places;
};
int main()
{
//城市
City* city = new City();
//景点
IPlace* bellTower = new BellTower();
IPlace* warriors = new TerracottaWarriors();
//访问者
IVisitor* tourist = new Tourist();
IVisitor* cleaner = new Cleaner();
//添加景点
city->Attach(bellTower);
city->Attach(warriors);
//接收访问
city->Accept(tourist);
city->Accept(cleaner);
system("pause");
return 0;
}
四,优缺点
优点:
- 增加新的访问操作很方便。使用访问者模式,增加新的访问操作就意味着增加一个新的具体访问者类,实现简单,无须修改源代码,符合“开闭原则”。
- 将有关元素对象的访问行为集中到一个访问者对象中,而不是分散在一个个的元素类中。类的职责更加清晰,有利于对象结构中元素对象的复用,相同的对象结构可以供多个不同的访问者访问。
- 让用户能够在不修改现有元素类层次结构的情况下,定义作用于该层次结构的操作。
缺点:
- 增加新的元素类困难。每增加一个新的元素类都意味着要在访问者中增加一个新的操作,并在每一个具体访问者类中增加相应的具体操作,这违背了“开闭原则”的要求。
- 破坏封装。访问者模式要求访问者对象访问并调用每一个元素对象的操作,这意味着元素对象有时候必须暴露一些自己的内部操作和内部状态,否则无法供访问者访问。
五,使用场景
1、对象结构中对象对应的类很少改变,但经常需要在此对象结构上定义新的操作。 2、需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类,也不希望在增加新操作时修改这些类。
注意事项:访问者可以对功能进行统一,可以做报表、UI、拦截器与过滤器。