以下面的简化C ++类层次结构为例。我想要完成的是Service
提供了一个用于保存任意Model
个对象的虚拟方法。但是Service
的每个子类,例如BoxService
应该而且只能保存Box
个对象。
由于C ++不支持方法参数中的协方差,我不能简单地在BoxService.h
中声明保存方法,如:
void save(Box box);
我的问题是,该问题是否有任何首选的设计模式或最佳做法?或者,如果到达的Model对象的类型为Box并且抛出异常,我应该在BoxService.cpp
中检查save函数的实现吗?
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'
}
答案 0 :(得分:1)
所以你听起来像是想按模型类型对操作实现进行分组。我将解释更多的OOP方法。
将Service
与实现分开,但我们将摆脱讨厌的参数:
class Service { ... };
class ServiceImpl {
virtual ~ServiceImpl() {}
void save() const = 0;
void remove() const = 0;
};
每个实现都是轻量级的并且支持操作,但是会在构造函数中使用参数:
class BoxService : public ServiceImpl {
Box _b;
public:
BoxService(Box b) : _b(b) {}
void save() const { ... }
void remove() const { ... }
};
现在我们有一个抽象工厂来创建我们需要的实现:
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");
}
}
};
现在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();
}
};
进一步的步骤:
答案 1 :(得分:1)
这可能是一种更具功能性的方法:
将每种模型类型与其实现配对:
template<typename T, typename ExtraType>
struct WithType {
T value;
using extra_type = ExtraType;
WithType(T value) : value(value) {}
};
将Model
定义为变体而不是继承层次结构:
using Model = std::variant<WithType<Box, BoxService>, WithType<X, XService>>;
现在访问变体:
class Service {
public:
void save(Model m) const {
visit([](auto withService) {
typename decltype(withService)::extra_type service;
service.save(withService.value);
}, m);
}
void remove(Model m) const {
visit([](auto withService) {
typename decltype(withService)::extra_type service;
service.remove(withService.value);
}, m);
}
};