mstrkrft
mstrkrft

Reputation: 53

Hide the code to choose Derived class of Interface class?

Lets assume I have an interface class like

class car {
  public: 
    virtual void move(double x, double y) = 0;
  // etc etc
};

and a lot of derived classes like

class model8556 : virtual public car {
  private: 
    void move(double x, double y) {
      // ...
    }
  // etc etc
};

and I choose the model via

car* myCar;    
switch (foo) {
  case 1: myCar = new model3434(); break;
  case 2: myCar = new model8295(); break;
  // etc
}

Is there a good way to hide the switch code and maybe move it to the car class itself? I could wrap another class(?) around car (or just move the switch code to a global function?), but I wonder if there is something more elegant.

Any help is appreciated.

Upvotes: 2

Views: 71

Answers (5)

BlamKiwi
BlamKiwi

Reputation: 2193

Hiding the creation logic in a static method would be sufficient in your case.

static car* CreateInstance(int id)
 {
     case(id)
       // etc etc
 }

If you're interested, the pattern you're trying to get at is called the Factory Pattern and can be quite useful when the creation logic becomes much more complicated.

Upvotes: 1

Chris Drew
Chris Drew

Reputation: 15334

It is certainly worth extracting out the switch code to a separate function as you want to avoid duplicating it around your code.

You could put it in a static function on a class. Some people will put it on Car itself but I wouldn't recommend it as it couples Car to the derived classes and you have to be careful of circular dependencies. Some people will have it as a static function on a class of its own. This is sometimes described as a Simple Factory.

But in C++ there is nothing wrong with having it in a non-member function:

#include <memory>

class Car {
  public: 
    virtual ~Car() {}
    virtual void move(double x, double y) = 0;
};

class Model3434 : public Car {
  public: 
    void move(double, double) override { }
};

class Model8295 : public Car {
  public: 
    void move(double, double) override { }
};

std::unique_ptr<Car> createCar(int foo) {
    switch (foo) {
      case 1: return std::make_unique<Model3434>();
      case 2: return std::make_unique<Model8295>();
      default: return nullptr;
    }
}

int main() {
    auto car1 = createCar(1);
    auto car2 = createCar(2);
}

I've used unique_ptr instead of raw pointers as I think it is good practice for owning pointers but it should be roughly equivalent.

It may be preferable to pass an enum to your factory function instead of an integer.

Also it maybe worth having the factory function in a namespace, preferably in the same namespace as Car, to avoid polluting the global namespace.

Upvotes: 3

doctorlove
doctorlove

Reputation: 19232

I would be wary of putting the factory inside the car interface. You will need to change this any time you make a new car type.

If you keep the definition out of line, in a cpp file and put it in a static class as other answers have suggested this will work. However, if you do this, the car has a dependency, albeit indirect, on its subtypes. Keeping the creation function as a free function means you can add types without this drawback.

Rather than putting this in global namespace, you could use whichever namespace your car is in.

More generally, in terms of an interface - what is car's single responsibility if it is a car and a car creation factory? This to me suggests the function should be elsewhere.

Upvotes: 1

ravi
ravi

Reputation: 10733

There is a factory pattern to facilitate this kind of thing in c++.

Below is the Pseudo code for that:-

class CarFactory
{
  public:
       static Car* createNewCar(std::string cartype )
       {
         if ( cartype == bmw )
             return new BMW();
         if ( cartypr == GALLARDO )
             return new GALLARDO ();
       }
};

In calling routine you can do

BMW* bmw = CarFactory::createNewCar( bmw );

It's basically same as your if/else block, the only thing is that it localizes the object creation at one place so as to avoid maintenance headaches.

Upvotes: 1

Nathan White
Nathan White

Reputation: 1080

This looks like you want a factory pattern and supply the create() (or whatever it's called) with a model type (maybe an enum?).

// .hpp

class factory
{
    public:
        static car* create(int model);
}

// .cpp

car* create(int model)
{
    switch(model)
    {
        // decide which car to make
    }
}

Note: code has not even slightly been tested, so my apologies if it doesn't work as-is.

Upvotes: 0

Related Questions