mercredi 20 mai 2015

Inheritance on Classes that contain another Derived classes

Having the next classes as a simplifiacation of the issue:

struct Car
{
    virtual int get_price() = 0;
};

struct ExpensiveCar: public Car
{
    int get_price( ) {/*..*/ }
    void apply_turbo( ){/*..*/};
};

struct CheapCar: public Car
{
    int get_price( ) {/*..*/}
};

struct CarRetailer
{
    virtual std::vector<Car*> get_cars( ) = 0;
};


struct ExpensiveCarsRetailer : public CarRetailer
{
    virtual std::vector<Car*> get_cars( ) { /*..*/ }
    std::vector<ExpensiveCar*> get_cars_for_exhibitions( );
};

struct CheapCarsRetailer : public CarRetailer
{
    virtual std::vector<Car*> get_cars( ) { /*..*/ }
    std::vector<CheapCar*> get_second_hand_cars( );
};

The rules are: Expensive Cars are only sold in ExpensiveCarsRetailers (similar for Cheap Cars ). Cheap cars do not have turbo and expensive cars are not sold in second hand.

The issue I face here is the coupling of classes that contains inherited classes as well. Therefore, if ExpensiveCarRetailer inherits from CarRetailer, it will need to implement virtual std::vector<Car*> get_cars( ) that is actually returning a vector of Car*, however, internally ExpensiveCarRetailer only created objects of ExpensiveCar. Furthermore, the get_cars_for_exhibitions() is not included in the public interface CarRetailertherefore, it can return a std::vector<ExpensiveCar*> instead.

The mixture in the API ( returning vector of Car* and ExpensiveCar* ) is very ugly, and so is the code that the user needs to write to use the apply_turbo( ) function of the list of cars from a specific ExpesiveCarsRetailer.

ExpensiveCarsRetailer ferrari;

std::vector<Car*> car = ferrari.get_cars();
ExpensiveCar* expensive_car;
for( int i = 0; i < car.size( ); ++i)
{
expensive_car = dynamic_cast<ExpensiveCar*>(car[i]);
expensive_car->apply_turbo();
}

I am sure I am missing some design pattern that helps in this situation, where to trees of class inheritances are coupled in a way that the abstract class of one of the inheritance tress need to return a vector (or set, list, etc ) of classes on the other inheritance tree. I am trying to avoid the dynamic casting as much as possible.

I also though on making CarRetailer a templated class, therefore:

template struct CarRetailer { virtual std::vector get_cars( ) = 0; };

And then make:

struct ExpensiveCarRetailer: public CarRetailer<ExpensiveCar>
{
...
}

But I don't think this would work since, for example, the CarRetailer can also start selling motorbikes ( similar structure as cars ) or bikes etc ( always applying the Expensive/Cheap pattern ) and eventually the number of template classes that need to be defined in CarRetailer will be huge.

Aucun commentaire:

Enregistrer un commentaire