首页 > 解决方案 > 有没有一种优雅的方法来处理运行时依赖的对象组合而不使用 dynamic_cast?

问题描述

语境

我正在尝试设计一个模块化且易于扩展和测试的建模框架。(我正在尝试遵循开放/封闭、依赖注入和组合而不是继承原则)。

我有一个模型对象的层次结构,其中包含方法,有些是所有人共有的,有些是特定于某些模型的。我还有一系列估计方法,它们适用于模型的各种子集。

每个估算器类都需要一个模型对象,该对象使用构造函数传递。

虽然在编译时构造对象没有问题,但我正在努力想出一种在运行时这样做的方法,而不是求助于估算器中父模型接口的 dynamic_casting。虽然它有效,但如果将模型和估计器的无效组合放在一起,则可能是不安全的。我还读到 dynamic_casting 通常是代码设计不佳的标志。

问题

当对象组合细节仅在运行时才知道时,是否有任何标准设计模式或技术可以避免使用dynamic_cast ?

或者这是否表明对象层次结构需要完全重新设计以避免对象之间的这种条件依赖?

代码示例

#include <iostream>
#include <unordered_map>

class i_model {
public:
    virtual double getparameter_a() = 0;
 };

class i_model_A : public i_model {
public:
    virtual double getparameter_b() = 0;
};

class model_A : public i_model_A {
public:
    double getparameter_a() { return 4.0; };
    double getparameter_b() { return 4.0; };
};

class i_model_B : public i_model {
public:
    virtual double getparameter_c() = 0;
};

class model_B : public i_model_B {
public:
    double getparameter_a() { return 5.0; };
    double getparameter_c() { return 2.0; };
};

class i_estimator {
public:
    virtual double getestimate() = 0;
};

class estimator_1 : public i_estimator {
public:
    estimator_1(i_model* in) : model(dynamic_cast<i_model_A*>(in)) {};
    double getestimate() { return model->getparameter_a() + model->getparameter_b(); };
private:
    i_model_A* model;
};

class estimator_2 : public i_estimator {
public:
    estimator_2(i_model* in) : model(dynamic_cast<i_model_B*>(in)) {};
    double getestimate() { return model->getparameter_a() + model->getparameter_c(); };
private:
    i_model_B* model;
};

int main()
{
    int a;
    int b;
    i_model* model = nullptr;
    i_estimator* estimator = nullptr;

    std::cout << "Select model: ";
    std::cin >> a;

    std::cout << "\nSelect estimator: ";
    std::cin >> b;
    
    switch(a) {
    case 1: model = new model_A(); break;
    case 2: model = new model_B(); break;
    }

    switch (b) {
    case 1: estimator = new estimator_1(model); break;
    case 2: estimator = new estimator_2(model); break;
    }
    std::cout << "\nEstimator value = " << estimator->getestimate();
} 

标签: c++compositiondynamic-cast

解决方案


据我了解,某些估算器仅适用于某些类型的模型,但是您允许用户在运行时选择他们想要的任何估算器和模型,然后您尝试将它们粉碎在一起,无论它们是否适合。

如果这是您想要的行为,那么无论用户如何选择不兼容的模型和估计器,因此无论您编写哪种代码,都可能在运行时失败。在这种情况下,运行时失败是正确的行为,因此无法使用类型系统(或任何其他方式)消除这种可能性。

编辑:上面是稍微不正确的,因为当转换失败时dynamic_cast返回nullptr指针,然后你得到一个空指针取消引用,它是 UB。如果您使用引用而不是指针,那将是正确的,在这种情况下,失败dynamic_cast会引发异常。或者,如果您想使用指针,请对结果进行显式空检查并抛出。


推荐阅读