【问题标题】:c++ composition (has-a) issuec++ 组合(has-a)问题
【发布时间】:2013-01-28 09:07:18
【问题描述】:

作为一名 C++ 程序员,我学到的一个重要且基本的规则是组合优先于继承 (http://en.wikipedia.org/wiki/Composition_over_inheritance)。

我完全同意这条规则,它使事情比使用继承要简单得多。

我有一个问题应该使用 Composition 来解决,但我真的很难做到。

假设您有一个供应商机器,并且您有两种类型的产品:

  1. 离散产品 - 像零食。
  2. 流体产品 - 像饮料。

这两种类型的产品需要在一个名为 VendorCell 的类中表示,该类包含单元格内容。

这两个产品共享一些相同的属性(dm),如价格、数量等...也包含一些不同的属性。

因此在这里使用合成可能会导致以下结果:

class VendorCell {
private : // default access modifier
    int price;
    int quantity;

    // int firstProductAttributeOnly
    // char secondProductAttributeOnly
};

如您所见,注释行表明,对于单个 VendorCell 取决于它包含的产品,这两个注释行中只有一个是重要且可用的(另一行仅与另一种类型 - 例如流体)。

因此,我可能有一个 VendorCell,里面有零食,并且不需要它的 secondProductAttributeOnly。

组合(对于 VendorCell)是正确的解决方案吗?对你们来说,有人会通过构造函数确定 VendorCell 类型,而一个 DM(专用于另一种类型的 DM)根本不会被使用(例如,将其标记为 -1),这对你们来说是否合适?>

谢谢大家!

【问题讨论】:

  • 嗯?...再说一遍,嗯?为什么不只拥有一个 VendorObject,然后从 VendorObject 继承 VendorSnack 和 VendorDrink。
  • 不要为了使用规则或模式而使用规则或模式。使用最合适的;不要弯曲东西以迫使它们进入 unnaturla 模具
  • 我认为vendorcell 有一个productsnack 是一个product。你明白了吗?
  • 所以你认为只有在“函数”行为不同时才应该使用继承?我认为这是错误的。多态性不仅仅与函数有关。也是关于数据的。
  • @Marius Bancilla:“也与数据有关”

标签: c++ polymorphism containers smart-pointers


【解决方案1】:

您倾向于组合而不是继承的一般规则是正确的。这里的问题是你想要一个container of polymorphic objects,而不是一个可以容纳所有可能产品的巨大聚合类。但是,由于slicing problem,您不能直接保存多态对象,而是需要通过(最好是智能)指针来保存它们。您可以通过(智能)指针直接持有它们,例如

class AbstractProduct { /* price, quauntity interface */ };
class AbstractSnack: public AbstractProduct { /* extended interface */ };
class AbstractDrink: public AbstractProduct { /* extended interface */ };
typedef std::unique_ptr<AbstractProduct> VendorCell;
typedef std::vector< VendorCell > VendorMachine;

您只需通过从 AbstractSnack/AbstractDrink 派生来定义您的零食/饮料

class SnickersBar: public AbstractSnack { /* your implementation */ };
class CocaColaBottle: public AbstractDrink { /* your implementation */ };

然后您可以像这样插入或提取产品:

// fill the machine
VendorMachine my_machine;
my_machine.emplace_back(new SnickersBar());
my_machine.emplace_back(new CocaColaBottle());

my_snack = my_machine[0]; // get a Snickers bar
my_drink = my_machine[1]; // get a Coca Cola bottle;

还有其他解决方案,例如Boost.Any,它使用内部保存指向多态对象的指针的包装类。您还可以通过将typedef 替换为包含std::vector&lt; VendorCell &gt; 的单独类VendorMachine 来重构此代码,这样您就可以获得更好的界面(例如具有货币兑换功能)

【讨论】:

  • 我可以将此技术用于供应商机器模式吗?持有一个指针作为 BaseMode 类的数据成员,它将被其他两种模式(MaintenanceMode、SellMode)继承——每个模式都定义了虚函数。
  • @SyndicatorBBB 如果你想实现依赖于状态的行为,你可能想研究像State这样的设计模式:sourcemaking.com/design_patterns/state我相信这个链接有一个自动售货机的例子:-)
  • 感谢您的详细解释。帮了我很多!
  • 我可以用这个原理做很多事情!!不仅仅是国家。我可以持有另一个系统的指针来更新需要在当前系统和报表系统中更新的报表。真是强大的工具!谢谢!
【解决方案2】:

你继承是为了被重用。

你创作是为了重复使用。

如果你有不同的属性,那么你可能想要继承,否则 compose。

一些变化:

class ProductVariety {
public:
    virtual void display(Screen& screen) = 0;
};

一个实现:

class Liquid : public ProductVariety {
public:
    virtual void display(Screen& screen) {
        //...
    }
}

作曲变奏:

class Product
{
    int price;
    int quantity;

    unique_ptr<ProductVariety> variety;
}

【讨论】:

    猜你喜欢
    • 2010-10-02
    • 1970-01-01
    • 1970-01-01
    • 2016-03-23
    • 1970-01-01
    • 2022-11-18
    • 2011-07-29
    • 1970-01-01
    相关资源
    最近更新 更多