Reputation: 53
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
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
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
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
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
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