【发布时间】:2019-12-14 19:07:35
【问题描述】:
我目前正在处理多态对象具有注入依赖项的情况,该依赖项也是多态的。我的问题是关于第一个类家族具有该家族共有行为的最佳方式,需要调用首先在第二个家族的顶级基类中定义的虚拟方法。具体来说,关于将拥有的智能指针存储到依赖项的位置——在基类、派生类或这两个地方。 (这个问题是关于在任务中使用智能指针,但是在使用引用或原始指针进行 DI 时也存在类似的问题。)
我将使用一些示例基类 Worker 和 Job 来说明。每个 Worker 拥有一个 Job,构造函数注入。 DerivedWorkers 可能要求用户注入特定的 DerivedJob,并调用特定于该 DerivedJob 的方法。每个 Worker 都必须有公共方法 get_location(),该函数的逻辑对所有 Worker 都是一样的,它需要调用 Job 中定义的虚拟方法 get_hours() 并被其子级覆盖。这是我提出的三种策略,“基址和派生的指针”、“仅派生的指针”和“仅基址的指针”:
class Job
{
public:
virtual ~Job();
virtual Hours_t get_hours();
};
class DerivedJob : public Job
{
public:
virtual Hours_t get_hours();
void derived_specific_method();
};
策略 1:基指针和派生指针
class Worker
{
public:
Worker(std::shared_ptr<Job> job) : my_job(job) {}
virtual ~Worker();
Location_t get_location()
{
return some_logic(my_job->get_hours());
}
private:
std::shared_ptr<Job> my_job; //cannot be unique_ptr
};
class DerivedWorker : public Worker
{
public:
DerivedWorker(std::shared_ptr<DerivedJob> derivedJob) : Worker(derivedJob), my_derived_job(derivedJob) {}
void derived_specific_duty()
{
my_derived_job->derived_specific_method();
}
private:
std::shared_ptr<DerivedJob> my_derived_job;
};
策略 2:仅派生指针
class Worker //abstract
{
public:
virtual ~Worker();
virtual Location_t get_location() = 0;
};
class DerivedWorker : public Worker
{
public:
DerivedWorker(std::unique_ptr<DerivedJob> derivedJob) : my_derived_job(derivedJob) {}
virtual Location_t get_location()
{
return some_logic(my_derived_job->get_hours());
}
void derived_specific_duty()
{
my_derived_job->derived_specific_method();
}
private:
std::unique_ptr<DerivedJob> my_derived_job;
};
策略 3:仅在基址中的指针
class Worker
{
public:
Worker(std::unique_ptr<Job> job) : my_job(job) {}
virtual ~Worker();
Location_t get_location()
{
return some_logic(my_job->get_hours());
}
protected:
std::unique_ptr<Job> my_job;
};
class DerivedWorker : public Worker
{
public:
DerivedWorker(std::unique_ptr<DerivedJob> derivedJob) : Worker(derivedJob) {}
void derived_specific_duty()
{
dynamic_cast<DerivedJob*>(my_job.get())->derived_specific_method();
}
};
每个都有缺点,我想弄清楚我是否缺少第四种方法,是否有惯用的或“最佳”方法,或者我是否缺少一些重构技巧依赖模式的类型已过时。
对于 1,“基数和派生指针”,缺点是即使每个 Job 仅由一个 Worker 拥有,您也不能使用 unique_ptr,因为从技术上讲,每个 Worker 可以拥有多个指向同一个 Job 的智能指针。如果频繁移动 Worker,或者由于移动 shared_ptrs 引入的缓存内聚减慢,这可能是一个问题。这是我目前倾向于的策略。
对于 2,“仅派生的指针”的缺点是大量的代码重复。 get_location() 必须是虚拟的,尽管对于所有工人来说几乎完全相同的代码。此外,现在 Worker 可能必须是抽象的。 (在这个特定的示例中,您可以通过为 Location_t 设置一个空值来避免这种情况,但这在此问题的实际应用中并不总是可行的。)
对于 3,“仅在基址中的指针”,缺点是必须使用 dynamic_cast,这是一个巨大的代码异味是有原因的。巨大的运行时成本,必须添加对失败演员表的检查等。
【问题讨论】:
-
是否有一些特定的上下文需要如此压倒性地困难?
-
“每个工人都有一份工作”——那为什么要
std::shared_ptr? -
澄清你需要什么:添加一些用例,你的解决方案有什么问题和想要的结果?
-
一个更具体的用例是一个依赖项,它为具有相同目的但电气接口不同的一系列硬件实现驱动程序,以及一个实现您希望使用硬件完成的任务的依赖项。我想使用 Worker/Job 的类比,因为它似乎更容易理解。
-
@SergeyKolesnik 这正是将指针两次存储在基数和派生中的策略的缺点,因为使用 unique_ptr 变得不可能。没有这个障碍,选择会很清楚,我不会发布问题。
标签: c++ dependency-injection dependencies polymorphism smart-pointers