访问者模式是一个可行的解决方案。该解决方案有两个主要参与者:
- 元素:具有共同父级的不同类型,将接受访问者。在这种情况下,元素是
Cat
和Dog
共同的父母是Animal
。
- 访问者:将访问元素的类,并且可以调用元素特定的操作,因为它具有特定元素类型的句柄。
对于此示例,从元素(Animal、Cat 和 Dog)开始:
class Animal
{
public:
virtual ~Animal() {}
virtual void eat() = 0;
};
class Cat: public Animal
{
public:
void destroyFurniture();
void eat();
};
class Dog: public Animal
{
public:
void chaseTail();
void eat();
};
接下来,创建一个将“访问”每个元素的访客。访问者将知道它正在操作的类型,因此它可以在两个特定元素上使用方法,例如Cat::destroyFurniture()
和Dog::chaseTail()
:
class Visitor
{
public:
void visitDog( Dog& dog ) { dog.chaseTail(); }
void visitCat( Cat& cat ) { cat.destroyFurniture(); }
};
Animal
现在,添加一个接受 aVisitor
作为参数的纯虚方法void Animal::accept( Vistor& )
:这个想法是将 a 传递给 a Visitor
,Animal
并允许虚拟方法解析为特定的运行时类型。一旦解决了虚拟调用,实现就可以visit
调用Visitor
.
class Animal
{
public:
...
virtual void accept( Visitor& ) = 0;
};
class Cat: public Animal
{
public:
...
virtual void accept( Visitor& visitor ) { visitor.visitCat( *this ); }
};
请注意虚拟方法如何用于解析特定的元素类型,并且每个元素的accept
实现都将调用访问者上的方法。这允许执行基于类型分支而不使用dynamic_cast
,通常称为双重分派。
这是一个可编译的示例,演示了正在使用的模式:
#include <iostream>
#include <vector>
using std::cout;
using std::endl;
class Cat;
class Dog;
class Visitor
{
public:
void visitCat( Cat& cat );
void visitDog( Dog& dog );
};
class Animal
{
public:
virtual ~Animal() {}
virtual void eat() = 0;
virtual void accept( Visitor& ) = 0;
};
class Cat: public Animal
{
public:
void destroyFurniture() { cout << "Cat::destroyFurniture()" << endl; }
void eat() { cout << "Cat::eat()" << endl; }
void accept( Visitor& visitor ) { visitor.visitCat( *this ); }
};
class Dog: public Animal
{
public:
void chaseTail() { cout << "Dog::chaseTail()" << endl; }
void eat() { cout << "Dog::eat()" << endl; }
void accept( Visitor& visitor ) { visitor.visitDog( *this ); }
};
// Define Visitor::visit methods.
void Visitor::visitCat( Cat& cat ) { cat.destroyFurniture(); }
void Visitor::visitDog( Dog& dog ) { dog.chaseTail(); }
int main()
{
typedef std::vector< Animal* > Animals;
Animals animals;
animals.push_back( new Cat() );
animals.push_back( new Dog() );
Visitor visitor;
for ( Animals::iterator iterator = animals.begin();
iterator != animals.end(); ++iterator )
{
Animal* animal = *iterator;
// Perform operation on base class.
animal->eat();
// Perform specific operation based on concrete class.
animal->accept( visitor );
}
return 0;
}
产生以下输出:
Cat::eat()
Cat::destroyFurniture()
Dog::eat()
Dog::chaseTail()
请注意,在这个例子中,Visitor
是一个具体的类。但是,可以为 创建整个层次结构Visitor
,从而允许您基于 执行不同的操作Visitor
。
class Visitor
{
public:
virtual void visitCat( Cat& ) = 0;
virtual void visitDog( Dog& ) = 0;
};
class FurnitureDestroyingVisitor: public Visitor
{
virtual void visitCat( Cat& cat ) { cat.destroyFurniture(); }
virtual void visitDog( Dog& dog ) {} // Dogs cannot destroy furniture.
};
访问者模式的一个主要缺点是添加Elements
可能需要对Visitor
类进行更改。一般的经验法则是:
- 如果 Element 层次结构可能发生变化,则在基本 Element 类上定义操作可能会更容易。在此示例中,如果需要添加 、 和 ,则向 中添加虚拟方法可能
Cow
会Horse
更容易。Pig
doTypical
Animal
- 如果元素层次结构是稳定的,但在元素上运行的算法正在发生变化,那么访问者模式可能是一个很好的候选者。