【问题标题】:How to switch on subtypes in object-oriented language?如何在面向对象语言中打开子类型?
【发布时间】:2021-09-17 01:26:35
【问题描述】:

我主要来自函数式语言背景,我不知道如何在面向对象语言中做好这种模式。

假设我有一些 Animal 类和子类以及一个 AnimalManager。我想要一个 AnimalManager 上的“add”方法,它具有不同的行为,具体取决于我添加的是猫还是狗。像这样的东西(没有特定的语言):

class Animal
class Cat : Animal
class Dog : Animal

class AnimalManager {
  void add(Animal a) { 
    a match {
       case Cat => // do something with cats
       case Dog => // do different something with dogs
    } 
  }
}

现在,如果我使用像 C++ 这样不支持以这种方式匹配类型的语言怎么办。我能想到的两个解决方案是:

  1. 使用 dynamic_cast。 但是,有人告诉我应该避免动态转换,因为它会导致运行时错误,所以这可能是不好的做法。
  2. 使用虚函数。我可以做类似以下的事情(同样没有特定的语言,但你应该明白要点):
class Animal {
  virtual void beAdded(AnimalManager am)
}
class Cat : Animal {
  void beAdded(AnimalManager am) {
    am.doThingsWithCats(this)
  }  
}
class Dog : Animal {
  void beAdded(AnimalManager am) {
    am.doThingsWithDogs(this)
  }  
}

class AnimalManager {
  void doThingsWithDogs(Dog d) {
    // do something with dogs
  }
  void doThingWithCats(Cat d) {
    // do something with cats
  }
  void add(Animal a) {
    a.beAdded(this)
  } 
}

这显然是非常尴尬的,还有其他问题(现在 Animal 需要了解 AnimalManager?)。

在面向对象语言中实现这种模式的最佳方法是什么?这里有一些我应该知道的“设计模式”吗?

【问题讨论】:

  • 从 OO 的角度来看,尴尬的是在添加猫和狗时对它们做一些不同的事情,而不是在添加它们后为您想要对它们做的事情使用虚拟函数。
  • 有点切线但是...what is AnimalManager?而且您编写尝试的解决方案的方式似乎在某种程度上重塑了访问者(一种常见的设计模式)。
  • 你没有。您使用虚拟方法,根据需要在每个子类型中执行不同的操作。任何时候您需要知道实际的子类型,您都可能做错了。
  • @KidKush420:如果AnimalManager 需要知道它管理的是哪种动物,那么Animal 在其作为基类的工作中失败,或者@ 987654327@ 管理Animals 的工作失败了。任何需要 Animal 的东西都不应该关心它是哪一种,如果是这样,你做错了什么或者你不应该使用 OOP。
  • @KidKush420: "它只是对动物做一些非特定的事情" 但它对Animals 做了一些非常特定的事情.如此具体以至于它需要知道它是什么类型的Animal 才能完成它的工作。问题是它为什么需要这个。

标签: c++ oop design-patterns types


【解决方案1】:

我建议你提供真实的用例而不是隐喻,因为你可能有一个XY problem。 您可以在这里使用 RTTI,还有一个问题我想详细说明。

不要将void add(Animal a) 哪个slices 用于您的对象只是Animal,您将始终得到Animal 而不是派生类CatDog。 使用void add(Aniaml& a) 保留对象或使用(智能)指针。

#include <iostream>

class Animal{
public:
// Read Item 7 in Scott Meyers' Effective C++ if you don't know why you need it
#include <iostream>

class Animal{
public:
    virtual ~Animal(){};
};
class Cat : public Animal{

};
class Dog : public Animal{

};

class AnimalManager {
public:
#define REF < comment it to see what your code do
#ifdef REF
  void add(Animal& a) { 
#else
  void add(Animal a) { 
#endif
    using std::cout;
    using std::endl;
    if(typeid(a) == typeid(Cat)){
        cout << "Meow" << endl;
    }else if(typeid(a) == typeid(Dog)){
        cout << "Bark" << endl;
    }else{
        cout << "I'm just animal" << endl;
    }
  }
};
int main()
{
    Dog d;
    Cat a;
    AnimalManager m;
    m.add(d);
    m.add(a);
}

Live demo

参考

Bark
Meow

按值传递

"I'm just animal
"I'm just animal

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-05-19
    • 1970-01-01
    • 2011-06-09
    • 2020-03-22
    • 1970-01-01
    • 1970-01-01
    • 2011-08-01
    • 2010-09-06
    相关资源
    最近更新 更多