c++ - 有没有一种优雅的方法来处理运行时依赖的对象组合而不使用 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();
}
解决方案
据我了解,某些估算器仅适用于某些类型的模型,但是您允许用户在运行时选择他们想要的任何估算器和模型,然后您尝试将它们粉碎在一起,无论它们是否适合。
如果这是您想要的行为,那么无论用户如何选择不兼容的模型和估计器,因此无论您编写哪种代码,都可能在运行时失败。在这种情况下,运行时失败是正确的行为,因此无法使用类型系统(或任何其他方式)消除这种可能性。
编辑:上面是稍微不正确的,因为当转换失败时dynamic_cast
返回nullptr
指针,然后你得到一个空指针取消引用,它是 UB。如果您使用引用而不是指针,那将是正确的,在这种情况下,失败dynamic_cast
会引发异常。或者,如果您想使用指针,请对结果进行显式空检查并抛出。
推荐阅读
- scala - 使用带有内部特征的 Scalacheck
- r - 在 R 中,使用矩阵列表,如何快速找到列表中每个矩阵的第一行和第二行之间的差异?
- javascript - JS 全局变量不起作用
- vba - 运行时错误“9”:下标超出范围 VBA Excel 2003
- python - 如何根据以列表为值的列过滤 Pandas 数据框中的行?
- c++ - 如何在使用模板时将cpp代码编译成库文件
- android - Dagger2:编译错误:没有@Inject 构造函数或@Provides-annotated 方法无法提供
- python - 将 txt 文件的行与输出连接到另一个 txt 文件。Python
- php - 从 php 插入到 sql 给出 SQLSTATE[HY093] 错误
- javascript - 如果在 React 15 中嵌套 2 的最佳方法是什么?