假设我有一个“动物”类型的抽象对象。动物有公共纯虚方法“吃”。
我想将Animal派生成一个“狗”和“猫”,每个都有一个扩展的界面。例如,我希望Dog有一个公共方法“chaseTail”和Cat有一个公共方法“destroyFurniture”。
我想在“世界”物体中制作动物的集合。
我需要能够使用“getAnimalAtPosition”方法从World中检索这些动物,并能够在我获得的动物上任意调用chaseTail或destroyFurniture。
我想避免缓慢的dynamic_cast,测试位置是否是给定的动物或将chaseTail和destroyFurniture悬挂到Animal中,但我似乎在这里支持角落。
还有其他办法吗?
答案 0 :(得分:4)
visitor pattern是一个可行的解决方案。该解决方案有两个主要参与者:
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
添加纯虚方法,接受Visitor
作为参数:void Animal::accept( Vistor& )
。我们的想法是将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 ); }
};
注意虚方法如何用于解析特定的Element类型,以及每个元素的accept
实现将如何调用Visitor上的方法。这允许执行基于类型的分支而不使用dynamic_cast
,并且通常被称为double dispatch。
Here是一个可编辑的示例,演示了正在使用的模式:
#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
类。一般的经验法则是:
Cow
,Horse
和Pig
,则可能更容易向doTypical
添加虚拟Animal
方法。 答案 1 :(得分:1)
你可以在完全了解所有课程的情况下做一个访客模式。
答案 2 :(得分:0)
在运行时发现多态层次结构的动态类型的愿望通常表示设计错误。你可以尝试这样的事情:
struct Animal
{
virtual ~Animal() { }
void eat() { doEat(); }
void performTypicalActivity() { doTypical(); }
private:
virtual void doEat() = 0;
virtual void doTypical() = 0;
};
struct Cat : Animal
{
void destroyFurniture();
void purr();
private:
virtual void doTypical() { destroyFurniture(); purr(); }
};
现在,您遍历集合并在每个元素上调用p->performTypicalActivity()
。