c++ - 使用 "irrelevant"方法的继承类设计

标签 c++ inheritance composite virtual-functions

假设我有以下抽象基类:

class DLAContainer {
public:
    DLAContainer() { std::random_device rd; mt_eng = std::mt19937(rd()); }
    virtual void generate(std::size_t _n) = 0;
protected:
    std::mt19937 mt_eng;

    virtual void spawn_particle(int& _x, int& _y,
        std::uniform_real_distribution<>& _dist) = 0;
    virtual void spawn_particle(int& _x, int& _y, int& _z,
        std::uniform_real_distribution<>& _dist) = 0;

    // ... among other methods to be overridden...
};

和继承自 DLAContainer 的两个类:

class DLA_2d : public DLAContainer {
public:
    DLA_2d() : DLAContainer() { // initialise stuff }
    void generate(std::size_t _n) { // do stuff }
private:;
    std::queue<std::pair<int,int>> batch_queue;
    // ...

    void spawn_particle(int& _x, int& _y, 
        std::uniform_real_distribution<>& _dist) { // do stuff }
    void spawn_particle(int& _x, int& _y, int& _z,
        std::uniform_real_distribution<>& _dist) { // do nothing }

    //...
};

class DLA_3d : public DLAContainer {
public:
    DLA_3d() : DLAContainer() { // initialise stuff }
    void generate(std::size_t _n) { // do stuff }
private:;
    std::queue<std::tuple<int,int,int>> batch_queue;
    // ...

    void spawn_particle(int& _x, int& _y, 
        std::uniform_real_distribution<>& _dist) { // do nothing }
    void spawn_particle(int& _x, int& _y, int& _z,
        std::uniform_real_distribution<>& _dist) { // do stuff }

    //...
};

如您所见,spawn_particle 有两个重载 - 一个用于 2D 晶格,另一个用于 3D,但是两者都是纯 virtual 函数,因此必须在 DLA_2dDLA_3d 子类中覆盖/实现,其中 3D 方法在 DLA_2d 中不执行任何操作,对于 DLA_3d 反之亦然

当然,这行得通,一切正常,但我不禁觉得设计有点笨拙,因为必须在每个类中实现不相关的方法。

是否有更好的设计模式,例如为两个派生类实现单独的接口(interface)(即 ISpawnParticle_2dISpawnParticle_3d)?还是在这种情况下更倾向于组合而不是继承?

编辑:我应该补充一点,DLAContainer 还有其他几种方法(和字段)。其中一些方法已定义(这样它们就可以被 DLA_2dDLA_3d 使用),其他方法是纯虚拟的,类似于 spawn_particle -这就是为什么在这种情况下我将 DLAContainer 作为抽象基类。

最佳答案

你是对的,它很笨拙。
这是 OO 设计中一个常见错误的结果:当子类型不能说是 IS A 父类型时,使用继承仅仅是为了避免代码重复。

目前您可以调用:

DLA_3d d3;
d3.spawn_particle(...) //The 2D version
//and
DLA_2d d2;
d2.spawn_particle(...) //The 3D version

似乎没有通过忽略调用和不执行任何操作而产​​生的“不良影响”。问题是调用 spawn_particle 的代码需要注意:

  • 调用该方法可能什么都不做。
  • 或者预先检查类型以了解调用哪个方法。

这两者都对调用者施加了不必要的额外知识/工作。并有效地使其更容易出错。

PS:请注意,在运行时抛出错误并不能真正修复设计。因为调用者现在只剩下:“调用该方法可能会抛出或预先检查类型...”


您可以通过多种方式改进您的设计。但最终您知道自己想要实现的目标,并且必须自己做出决定。
这里有一些想法:

  • 首先要考虑以下设计原则:优先组合而不是继承
    • 您无需继承即可重用代码:您可以包含/引用另一个对象的实例,并通过调用包含/引用的对象来卸载工作。
    • 您提到 DLAContainer 有许多其他字段和方法。其中有多少可以转移到不同的或多个类(class)?
  • 容器生成粒子真的有意义吗?容器的职责应该是盛东西。您是否遵循单一职责原则? (我对此表示怀疑。)
  • 考虑将每个 spawn_particle 方法移动到适当的子类。 (虽然我怀疑这会给你留下非常相似的问题。)
  • 开发“粒子”的抽象概念。然后两个“粒子生成器”可以具有相同的签名,但生成不同的“粒子”具体实例,即2D 粒子或 3D 粒子。

关于c++ - 使用 "irrelevant"方法的继承类设计,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37769502/

相关文章:

c++ - `*a` 和 `a[0]` 有什么区别?

C++:通过派生类调用重载虚拟,导致 NULL 调用/段错误

python - 继承时避免 __init__ 和 super 样板

java - 如何使用包含生成值的复合主键创建实体

C++ 继承和组合一起使用

C++ Armadillo : Get the ranks of the elements in a vector

C++多重定义 'using'别名

c++ - 实现具有自动递增实例属性的类的最 Pythonic 方式是什么?

java:确保该类型只有一个实例

c++ - 我应该通过右值引用返回一个右值引用参数吗?