C ++协变参数 - 设计模式

时间:2017-03-17 22:24:14

标签: c++ polymorphism covariance contravariance

以下面的简化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'
}

2 个答案:

答案 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();
    }
};

进一步的步骤:

  • 设计一个解决方案,它会产生编译时错误,而不是断言。
  • 允许添加更多模型类型和更多操作,而无需更改(多)现有代码。这应该等同于表达式问题。请注意,这种按模型类型分组操作的方法需要更广泛的更改来添加新操作,而不是添加新模型类型。对于使用访问者和按操作分组模型类型,情况正好相反。表达式问题有解决方案,例如object algebras,但它们可能更加模糊。

答案 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);
    }
};