涉及依赖项注入时,如何使用合成而不是继承?

时间:2018-10-01 09:38:11

标签: c++ oop inheritance dependency-injection composition

在我的程序中,我有很多作为类建模的检查器:检查RAM是否正常,检查磁盘是否正常,温度是否正常等。这些检查器有很多共同之处,因此我对它们进行了建模具有继承性:所有的共同点都归入基类CheckerBase,该基类由具有检查器特定功能和依赖性的专门类派生。

但是我经常读到组合应该优先于继承,所以我想知道在带有组合的C ++中如何做到这一点?

#include <chrono>
#include <iostream>
#include <thread>
#include <vector>
using namespace std;

/** Dependencies of various checkers that I pass in via dependency injection. */
struct ErrorReporter {
    void report_error(string myMsg) {
        cout << myMsg;
    }
};
struct TemperatureSensor {
    int get_cpu_temp() { return 42; }
    int get_disk_temp() { return 32; }
};
struct DiskStressor {
    void stress_disk() { }
};

/** Contains dependencies that are common to all checkers.. */
class CheckerBase {
public:
    CheckerBase(ErrorReporter* errReporter ) :
        mErrReporter(errReporter) { }

    virtual void runTest() = 0;
protected:
    ErrorReporter* mErrReporter;
};

/** Needs `TemperatureSensor` dependency. */
class TemperatureChecker : public CheckerBase {
public:
    TemperatureChecker(ErrorReporter* errReporter,
                       TemperatureSensor* tempSensor) :
        CheckerBase(errReporter), mTempSensor(tempSensor) { }

    void runTest() override {
        if (mTempSensor->get_cpu_temp() > 42) {
            mErrReporter->report_error("CPU too hot");
        }
     };
private:
    TemperatureSensor* mTempSensor;
};

/** Needs `TemperatureSensor` and `DiskStressor` dependencies. */
class DiskChecker : public CheckerBase {
public:
    DiskChecker(ErrorReporter* errReporter, TemperatureSensor* tempSensor,
                DiskStressor* diskStressor) :
        CheckerBase(errReporter), mTempSensor(tempSensor) { }

    void runTest() override {
        mDiskStressor->stress_disk();
        mTempSensor->get_disk_temp();
        if (mTempSensor->get_cpu_temp() > 32) {
            mErrReporter->report_error("HDD too hot after strees test");
        }
     };
private:
    TemperatureSensor* mTempSensor;
    DiskStressor* mDiskStressor;
};

/** Periodically runs each checker. */
class MasterChecker {
    public:
        MasterChecker() :
            mTempChecker { &mErrReporter, &mTempSensor },
            mDiskChecker { &mErrReporter, &mTempSensor, &mDiskStressor },
            mAllCheckers({&mTempChecker, &mDiskChecker}) {};

        void start() {
            // In reality I use a timer that continously runs each checker at
            // a certain interval.
            while (true) {
                for (CheckerBase *checker : mAllCheckers) {
                    checker->runTest();
                }
                this_thread::sleep_for(chrono::milliseconds(5000));
            }
        }
    private:
        ErrorReporter mErrReporter;
        TemperatureSensor mTempSensor;
        DiskStressor mDiskStressor;

        DiskChecker mDiskChecker;
        TemperatureChecker mTempChecker;

        vector<CheckerBase*> mAllCheckers;
};

int main() {
    MasterChecker master;
    master.start();
}

编辑:已更新,其中包括近似使用检查器的方式。 MasterChecker定期运行所有单个检查程序。它具有检查程序列表,并调用其runTest()成员函数-所有检查程序都将从其基类中覆盖。

1 个答案:

答案 0 :(得分:3)

  

...组成应优先于继承

这意味着,您可以选择其中任一位置,而更喜欢合成。在这种情况下,MasterChecker(正确)会按照您的建议组成各种具体的检查器。

各个检查器继承/实现抽象基类的事实不是问题,因为您不能编写接口。这里没有选择,建议也没有说即使在合成不是替代方法的情况下也不要使用继承。

您的建议实际上遭到警告的情况是:

class MasterChecker: public DiskChecker, public TemperatureChecker

滥用继承来聚合基类子对象。

在您的情况下,由于初始化顺序和菱形继承的原因,至少在没有任何更改的情况下,这仍然可能无法正常工作。