更新时间:2023-01-11 08:01:31
访问者模式是一个可行的解决方案.该解决方案有两个主要参与者:
The visitor pattern is a viable solution. The solution has two main participants:
Cat
和 Dog
,共同的父元素是 Animal
.Cat
and Dog
with the common parent being Animal
.对于此示例,从元素(动物、猫和狗)开始:
For this example, begin with the elements (Animal, Cat, and 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()
>:
Next, create a Visitor that will 'visit' each Element. The Visitor will know the type it is operating on, so it can use methods on both the specific Elements, such as Cat::destroyFurniture()
and Dog::chaseTail()
:
class Visitor
{
public:
void visitDog( Dog& dog ) { dog.chaseTail(); }
void visitCat( Cat& cat ) { cat.destroyFurniture(); }
};
现在,向 Animal
添加一个纯虚方法,它接受一个 Visitor
作为参数:void Animal::accept(Vistor&)
.这个想法是将 Visitor
传递给 Animal
,并允许虚拟方法解析为特定的运行时类型.一旦虚拟调用被解析,实现就可以调用 Visitor
上的特定 visit
方法.
Now, add a pure virtual method to Animal
that accepts a Visitor
as an argument: void Animal::accept( Vistor& )
. The idea is to pass a Visitor
to an Animal
, and allow the virtual method to resolve to the specific runtime type. Once the virtual call is resolved, the implementation can invoke the specific visit
method on the 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
的情况下根据类型执行分支,通常称为 双重派遣.
Notice how the virtual method is used to resolve to the specific Element type, and that that each element's accept
implementation will invoke a method on the Visitor. This allows for execution to branch based on type without the use of dynamic_cast
, and is commonly referred to as double dispatch.
这里是一个演示使用模式的可编译示例:
Here is a compilable example that demonstrates the pattern in use:
#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
执行不同的操作.
Please note that in this example, Visitor
is a concrete class. However, it is possible for an entire hierarchy to be created for Visitor
, allowing you to perform different operations based on the 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
类进行更改.一般的经验法则是:
One major drawback to the Visitor pattern is that adding Elements
may require making changes to the Visitor
classes. The general rule of thumb is:
Cow
、Horse
和Pig
,那么添加一个虚拟的doTypical可能更容易
方法到 Animal
.Cow
, Horse
, and Pig
need to be added, then it may be easier to add a virtual doTypical
method to Animal
.