【问题标题】:Preferring composition over inheritance in the diamond problem在钻石问题中更喜欢组合而不是继承
【发布时间】:2021-07-06 00:33:02
【问题描述】:

假设我们有 3 个类 ButtonClickableRectangle
它们都不是抽象的,都可以独立存在。它们都有一个position 成员变量。当Button 类从ClickableRectangle 继承时,它们应该共享一个公共position 变量。
我的实现:

struct Positionable {
    struct Position {
        int x, y;
    } position;
};
struct Rectangle: virtual public Positionable {
    // ...
};
struct Clickable : virtual public Positionable {
    // ...
};
struct Button : virtual public Positionable, public Rectangle, public Clickable {
    Button() {
        this->position.x = 1;
        assert(Rectangle::position.x == 1);
        assert(Clickable::position.x == 1);
        assert(Positionable::position.x == 1);
    }
} test;

这个问题(除了单词 position 的 3 种形式)是我觉得我打破了组合优于继承的原则,因为我继承只是为了包含一个变量。 是否存在具有相同行为的设计模式?在这种情况下如何使用合成?

编辑:

我正在寻找一个解决方案,其中Button 继承自ClickableRectangle,因为它是一种is-a 关系。 (这可能不是最好的例子,但假设它是真的)
我也不想从Positionable 继承,因为这是一种有关系。
我想要和我的实现一样的行为,所以:

  • 他们都有一个Position position
  • 当它们相互继承时,它们共享这个变量
  • 我不使用 setter/getter

基本上我想要虚拟变量之类的东西,但 c++ 还没有这个功能,所以我正在寻找一个明智的替代品。

【问题讨论】:

  • 使用policy based design会更好吗?
  • 这是一个有趣的设计,但我不确定我如何在这里申请。
  • 按钮是“形状”和“动作”策略的用户,可以填充“矩形”和“可点击”。
  • 不确定Clickable 应该有Position

标签: c++ oop design-patterns diamond-problem


【解决方案1】:

我没有完全看到您的问题,但是您可以使用参考来实现这一点。您可以执行以下操作:

struct Position{
    int x,y;
};

class Rectangle {
    public:
        Rectangle(Position& p) : pos(p) {}
        ~Rectangle() {}
        Position getPos(){ return pos; }
    protected:
        Position& pos;
};

class Clickable {
    public:
        Clickable(Position& p) : pos(p) {}
        ~Clickable() {}
        Position getPos(){ return pos; }
    protected:
        Position& pos;
};

class Button {
    public:
        Button(int x, int y) : pos({x,y}), a(this->pos), b(this->pos) {}
        ~Button() {}
        Position getPos(){ return pos; }
    protected:
        Position pos;
        Clickable a;
        Rectangle b;
};

虽然这很混乱。如果你问我,使用原始的 RectangleClickable 类也很痛苦。如果您喜欢,指针也可以实现这一点。

【讨论】:

  • 如果在 stackoverflow 上允许基于意见的问题,我的主要问题是它是否是反模式。问题是,如果我想包含更多变量,我需要创建一堆无用的结构并且必须从它们继承,这对于会看到我的代码的程序员来说可能很难理解,因为每个继承都可以隐藏任意数量的变量和函数。可能我想要虚拟变量之类的东西,但我希望有一个很好的解决方法。
【解决方案2】:

继承对is-a 关系建模。如果Horse 继承自Animal,那么Horse 是一个 Animal,因为动物的所有属性也是马的属性。

现在,在您的示例中,说 ButtonClickable 是否有意义?是的,所以继承是这项工作的适用工具。说ButtonRectangle 有意义吗?好吧,现在我们进入浑水。

如果我们打算通过它的Rectangle 子接口使用Button(也许一个简单的渲染器可以接受矩形派生对象并渲染它们),那么继承在这里也是合适的。但是如果使用继承因为它是正确的 OOP 并且这是需要做的事情,那么就不会。

权衡你需要什么,看看它是否合乎逻辑。想想是否应该,然后再考虑是否可以。

PS:我不明白为什么Clickable 不是抽象的。这听起来像一个界面。如果它不是一个接口,那么您尝试建模的概念可能有一个更好的名称。

编辑

我突然想到,如果您使用的是 C++,您可能需要查看private 继承。这就是您所说的实现-继承。 C++ 常见问题解答:https://isocpp.org/wiki/faq/private-inheritance

【讨论】:

  • 我的问题不在于 ButtonRectangleClickable 之间的关系,我希望给出这些。我不是很清楚这一点,所以我更新了我的问题。我不想从Positionable 继承的问题,因为它不是is-a 关系并且它没有任何功能,但我仍然想要共享的position 变量。
  • 感谢您更新问题,对不起,我误解了。在我看来,从Positionable 私下和虚拟地继承似乎是一种不错的方法。它具有您想要的语义,而不会将此继承暴露给外部。如果您想完全避免继承,那么您可以尝试将std::shared_ptr<Position> 作为每个类都不同的成员并将其设置为指向相同的位置实例,以便有效地共享它。我认为这个解决方案更糟糕,因为动态分配。
猜你喜欢
  • 1970-01-01
  • 2020-10-03
  • 1970-01-01
  • 2019-10-09
  • 2017-12-09
  • 2022-01-12
  • 1970-01-01
  • 2012-11-05
  • 1970-01-01
相关资源
最近更新 更多