假设我有一个“动物”类型的抽象对象。动物具有公共(public)纯虚拟方法“吃”。
我想将Animal派生为“狗”和“猫”,每一个都具有扩展的接口(interface)。例如,我希望Dog使用公用方法“chaseTail”,而Cat使用公用方法“destroyFurniture”。
我想在“世界”对象中收集动物。
我需要能够使用“getAnimalAtPosition”方法从“世界”中检索这些动物,并能够在获得的动物上任意调用chaseTail或destroyFurniture。
我想避免缓慢的dynamic_cast,测试位置是否是给定的动物,或者将chaseTail吊起并将destroyFurniture提升为Animal,但是我似乎在这里陷入了困境。
还有另一种方法吗?
最佳答案
visitor pattern是一个可行的解决方案。该解决方案有两个主要参与者:
Cat
和Dog
,公共(public)父对象是Animal
。 对于此示例,从元素(动物,猫和狗)开始:
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
可能会更容易。 关于c++ - 具有扩展接口(interface)的派生类的集合。如何在不进行动态强制转换的情况下访问派生接口(interface)?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/11125475/