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

At what inheritance level to store a dependency pointer when both dependency and dependent are polymorphic?

我目前正在处理一种情况,其中多态 object 具有注入的依赖项,该依赖项也是多态的。我的问题是关于第一个 classes 系列具有该系列共有行为的最佳方式,需要调用首先在第二个系列的顶层基 class 中定义的虚拟方法。具体来说,关于将拥有的智能指针存储到依赖项的位置 - 在基础中,在派生的 classes 中,或在这两个地方。 (这个问题是关于使用智能指针的任务,但是使用引用或原始指针进行DI时存在类似的问题。)

我将使用一些示例基础 classes Worker 和 Job 来说明。每个 Worker 拥有一个 Job,constructor-injected。 DerivedWorkers 可能需要用户注入特定的 DerivedJob,并调用特定于该 DerivedJob 的方法。每个Worker都必须有public方法get_location(),这个函数的逻辑对所有Worker都是一样的,需要调用Job中定义的一个虚方法get_hours()并被其 children 覆盖。这是我想出的三种策略,"pointer in base and derived," "pointer in derived only," 和 "pointer in base only":

class Job
{
public:
    virtual ~Job();
    virtual Hours_t get_hours();
};

class DerivedJob : public Job
{
public:
    virtual Hours_t get_hours();
    void derived_specific_method();
};

策略一:基类和派生类指针

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

每个都有缺点,我想弄清楚是否还有第四种方法我遗漏了,是否有惯用的或 "best" 方法,或者我是否遗漏了一些重构使这种类型的依赖模式过时的技巧。

对于 1,"pointers in base and derived," 缺点是你不能使用 unique_ptr 即使每个 Job 只由一个 Worker 拥有,因为每个 Worker 在技术上可以拥有多个智能指针指向同一个工作。如果 Worker 频繁移动,或者作业在 Worker 之间交换,这可能会成为一个问题,因为移动 shared_ptrs 会导致缓存内聚变慢。这是我目前倾向于的策略。

对于 2,"pointer in derived only," 缺点是大量的代码重复。 get_location() 必须是虚拟的,尽管所有 Worker 的代码几乎完全相同。另外,现在Worker可能要抽象了。 (在这个特定的例子中,您可以通过为 Location_t 设置一个空值来避免这种情况,但这在这个问题的实际应用中并不总是可行的。)

对于 3,"pointer in base only," 缺点是必须使用 dynamic_cast,这是一个巨大的代码味道是有原因的。巨大的运行时成本,必须添加对失败的转换案例的检查等。

我会选择 2 的变体。:

而不是 virtual Location_t get_location() = 0; 更好的是 virtual Job& get_job() = 0;,因此您可以在派生 class 中使用协变 return 类型,并且 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;
};

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

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

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

除此之外,我还质疑这些虚拟 classes 的用途 - 它们是否有用途?仅仅拥有一个共同的功能并不足以成为创建共享基础的理由 class。它实际上需要帮助。不要因为他们说 "object oriented is good".

就做基础 classes

另外,我也会考虑模板方法。在您的对象很多并且需要快速处理的情况下,面向对象往往达不到基于模板的方法。