具有扩展接口的派生类的集合.如何在没有动态转换的情况下访问派生接口? [英] Collection of derived classes with extended interfaces. How to access derived interface without dynamic cast?

查看:40
本文介绍了具有扩展接口的派生类的集合.如何在没有动态转换的情况下访问派生接口?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个Animal"类型的抽象对象.动物有公共纯虚方法吃".

Say that I've got an abstract object of type "Animal". Animal has the public pure virtual method "eat".

我想将 Animal 派生为Dog"和Cat",每个都有一个扩展接口.例如,我希望 Dog 有一个公共方法chaseTail",而 Cat 有一个公共方法destroyFurniture".

I want to derive Animal into a "Dog" and "Cat", each with an extended interface. For example, I want Dog to have a public method "chaseTail" and Cat to have a public method "destroyFurniture".

我想在世界"对象中制作一组动物.

I want to make a collection of animals in a "World" object.

我需要能够使用getAnimalAtPosition"方法从 World 中检索这些动物,并且能够在我得到的 Animal 上任意调用chaseTail 或 destroyFurniture.

I need to be able to retrieve these animals from World with a "getAnimalAtPosition" method, and be able to arbitrarily call chaseTail or destroyFurniture on the Animal I got.

我想避免使用缓慢的 dynamic_cast,测试位置是否是给定的动物或提升chaseTail 和destroyFurniture 到Animal,但我似乎在这里被退回到角落.

I'd like to avoid the slow dynamic_cast, testing if the position is a given animal or hoisting chaseTail and destroyFurniture into Animal, but I seem to be backed into a corner here.

还有别的方法吗?

推荐答案

访问者模式是一个可行的解决方案.该解决方案有两个主要参与者:

The visitor pattern is a viable solution. The solution has two main participants:

  • 元素:具有共同父级的不同类型,将接受访问者.在这种情况下,元素是 CatDog,共同的父元素是 Animal.
  • 访问者:将访问元素的类,并且可以调用特定于元素的操作,因为它具有特定元素类型的句柄.
  • Elements: The distinct types with a common parent that will accept a Visitor. In this case, the Elements are Cat and Dog with the common parent being Animal.
  • Visitor: The class that will visit Elements, and can invoke Element specific operations as it has a handle to the specific Element type.

对于此示例,从元素(动物、猫和狗)开始:

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:

  • 如果 Element 层次结构可能会发生变化,则在基本 Element 类上定义操作可能会更容易.在这个例子中,如果需要添加CowHorsePig,那么添加一个虚拟的doTypical可能更容易 方法到 Animal.
  • 如果元素层次结构稳定,但在元素上运行的算法正在发生变化,那么访问者模式可能是一个不错的选择.
  • If the Element hierarchy is likely to change, it may be easier to define operations on the base Element class. In this example, if Cow, Horse, and Pig need to be added, then it may be easier to add a virtual doTypical method to Animal.
  • If the Element hierarchy is stable, but the algorithms operating on the Elements are changing, then the Visitor pattern may be a good candidate.

这篇关于具有扩展接口的派生类的集合.如何在没有动态转换的情况下访问派生接口?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆