Take the following simplified C++ class hierarchy as an example. What I want to accomplish is that Service
provides a virtual method for saving arbitrary Model
objects. But each subclass of Service
, e.g. BoxService
should and can only save Box
objects.
Due to the fact that C++ does not support covariance in method parameters I cannot simply declare the save method in BoxService.h
like:
void save(Box box);
My question is, is there any preferred design pattern or best practice for that problem? Or should I check in the implementation of the save function in BoxService.cpp
if the arriving Model object is of type Box and throw an exception otherwise?
Model.h
class Model {
private:
int id;
};
Box.h
class Box : public Model {
private:
int size;
};
Service.h
class Service {
public:
virtual void save(Model model);
};
BoxService.h
class BoxService : public Service {
public:
void save(Model box);
};
BoxService.cpp
void BoxService::save(Model box) {
// TODO: save box and make sure that box is of type 'Box' and not any other subtype of 'Model'
}
So you sound like you want to group operation implementations by model type. I'll explain a more OOP approach.
Separate Service
from the implementations, but we're going to get rid of the pesky parameter:
class Service { ... };
class ServiceImpl {
virtual ~ServiceImpl() {}
void save() const = 0;
void remove() const = 0;
};
Each implementation will be lightweight and support the operations, but will take the parameter in the constructor:
class BoxService : public ServiceImpl {
Box _b;
public:
BoxService(Box b) : _b(b) {}
void save() const { ... }
void remove() const { ... }
};
Now we have an abstract factory to create implementations as we need them:
class ServiceImplFactory {
public:
std::unique_ptr<ServiceImpl> create(Model m) const {
if (auto b = dynamic_cast<Box*>(m)) {
return std::make_unique<BoxService>(*b);
} else if (auto x = dynamic_cast<X*>(m)) {
return std::make_unique<XService>(*x);
} else {
assert(!"Not all Models covered by Service implementations");
}
}
};
Now Service
:
class Service {
ServiceImplFactory _implFactory;
public:
void save(Model m) const {
return _implFactory.create(m)->save();
}
void remove(Model m) const {
return _implFactory.create(m)->remove();
}
};
Further steps: