当依赖项和依赖项都是多态时,在什么继承级别上存储依赖项指针?

问题描述 投票:0回答:2

我目前正在处理一个多态对象具有注入的依赖关系的情况,该依赖关系也是多态的。我的问题是有关第一个类家族具有与该家族相同的行为的最佳方式,这要求调用一个在第二个家族的顶级基类中首先定义的虚拟方法。具体来说,关于将拥有的智能指针存储到依赖项的位置-在基中,在派生类中或在两个地方。 (这个问题是关于为任务使用智能指针的,但是当将引用或原始指针用于DI时,也会存在类似的问题。)

我将使用一些示例基类Worker和Job进行说明。每个工人都拥有一个由构造函数注入的作业。 DerivedWorkers可能要求用户注入特定的DerivedJob,并调用特定于该DerivedJob的方法。每个Worker必须具有公共方法get_location(),并且此函数的逻辑对于所有Workers都是相同的,并且它需要调用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,“基础和派生指针”的缺点是,即使每个作业仅由一个Worker拥有,也不能使用unique_ptr,因为从技术上讲,每个Worker可以拥有指向同一Job的多个智能指针。如果由于移动shared_ptrs导致的缓存内聚速度减慢,而频繁移动Worker或在Jobs之间交换Jobs可能会出现问题。这是我目前倾向于的策略。

对于2,“仅派生指针”,缺点是大量的代码重复。尽管所有工人的代码几乎完全相同,但get_location()必须是虚拟的。另外,现在,Worker可能必须是抽象的。 (在此特定示例中,您可以通过为Location_t设置一个空值来避免这种情况,但这在此问题的实际应用中并不总是可行的。)

对于3,“仅基础指针”,缺点是必须使用dynamic_cast,这是一个巨大的代码味道,这是有原因的。巨大的运行时成本,必须为失败的转换案例添加检查,等等。

c++ dependency-injection dependencies polymorphism smart-pointers
2个回答
0
投票

我会选择2的变体。:

代替virtual Location_t get_location() = 0;更好地具有virtual Job& get_job() = 0;,因此可以在派生类中使用协变返回类型,并且get_location()的实现不重复。

class Worker // abstract
{
public:
    virtual ~Worker() = default;
    virtual Job& get_job() = 0;

    Location_t get_location() { return some_logic(get_job().get_hours()); }
};

class DerivedWorker : public Worker
{
public:
    explicit DerivedWorker(std::unique_ptr<DerivedJob> derivedJob) : my_job(std::move(derivedJob)) {}

    DerivedJob& get_job() override { return *my_job;}

    void derived_specific_duty() { my_job->derived_specific_method(); }
private:
    std::unique_ptr<DerivedJob> my_job;
};

0
投票

方法1-问题是数据重复,这既是开销又是bug的来源。

方法3-问题是动态转换-这是一个非常慢的转换。您本可以只使用static_cast,但要不断进行转换是很麻烦的。由于基类几乎没有实现,因此不值得在其中存储类型,因为它仅限制了其使用。

[方法2-是您提供的3种方法中唯一明智的方法。

除此之外,我还质疑这些虚拟类的目的-它们是否有作用?仅具有通用功能不足以构成共享基类的理由。它实际上需要帮助。不要仅仅创建基类,因为他们说“面向对象是好的”。

此外,我还将考虑模板方法。如果您的对象众多且需要快速处理,则面向对象的方法往往无法使用基于模板的方法。

© www.soinside.com 2019 - 2024. All rights reserved.