C ++ composition (has-a) problem

One of the important and important rules that I learned as a C ++ programmer is to prefer composition over inheritance ( http://en.wikipedia.org/wiki/Composition_over_inheritance ).

I totally agree with this rule, which basically makes things much easier than it would be if we were using Inheritance.

I have a problem to solve with composition, but I'm really trying my best to do it.

Suppose you have a Supplier and you have two types of products:

  • A discrete product is like a snack.
  • A liquid product is like a drink.

These two types of products must be represented in the VendorCell class, which contains the contents of the cells.

These two products have the same attributes (dm) like Price, Quantity, etc. BUT also contain several different attributes.

Therefore, using composition here can lead to the following result:

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

    // int firstProductAttributeOnly
    // char secondProductAttributeOnly
};

      

As you can see, the lines with comments show that for one VendorCell, depending on the product containing , only one of these two lines with comments will be meaningful and useful (the other is applicable only for the other type - liquid, for example).

So I can have a VendorCell with an appetizer inside and its secondProductAttributeOnly is not required.

Is composition (for VendorCell) the right solution? Does it seem right to you guys that someone will define the VendorCell type via the constructor and one DM (DM allocated to another type) won't be used at all (mark it as -1)? >

Thanks everyone!

+3


source to share


2 answers


Your general rule of thumb in favor of composition over inheritance is correct. The problem here is that you want a container of polymorphic objects , not a giant aggregate class that can contain all possible products. However, due to the slice problem, you cannot hold polymorphic objects directly, but you need to hold them with a (preferably smart) pointer, you can hold them directly with a (smart) pointer, for example

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;

      

You just define your snacks / drinks by getting from AbstractSnack / AbstractDrink

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

      



and then you can insert or extract products like this:

// 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;

      

There are other solutions, such as Boost.Any , which uses a wrapper class inside which there is a pointer to a polymorphic object. You can also refactor this code by replacing it typedef

with a separate class VendorMachine

it contains std::vector< VendorCell >

so that you can get a more user-friendly interface (for example, with a money exchange function).

+4


source


Inherited for reuse.

You create for reuse.

If you have different attributes, you probably want to inherit, otherwise compose.

Some variation:

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

      



Implementation:

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

      

Component variation:

class Product
{
    int price;
    int quantity;

    unique_ptr<ProductVariety> variety;
}

      

0


source







All Articles