涉及依赖注入时如何使用组合而不是继承?
How to use composition instead of inheritance when dependency injection is involved?
我的程序中有一堆检查器,我建模为 classes:检查 RAM 是否正常,检查磁盘是否正常,检查温度是否正常,等等。这些检查器有很多共同点,所以我用继承对它们进行建模:所有共同点都进入一个基础 class CheckerBase
,该基础 class 派生自具有检查器特定功能和依赖关系的专门 classes。
但是我经常读到组合应该比继承更受青睐,所以我想知道在 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();
}
编辑:已更新以包括如何使用跳棋的近似值。 A MasterChecker
定期运行所有单个检查程序。它有一个检查器列表并调用它们的 runTest()
成员函数——所有检查器都从它们的基础 class.
覆盖它
... composition should be preferred over inheritance
这意味着,你可以选择两者之一,但更喜欢合成。在这种情况下,MasterChecker
(正确地)组成了各种具体的检查器,正如您建议的那样。
单个跳棋 inherit/implement 抽象基础 class 的事实不是问题,因为您无法 组合 接口。这里别无选择,而且建议并没有说你永远不应该使用继承,即使组合不是替代方案。
您的建议实际警告的情况是:
class MasterChecker: public DiskChecker, public TemperatureChecker
其中滥用继承来聚合基础 class 子对象。
在你的情况下,由于初始化顺序和菱形继承的原因,这可能无论如何都不会很好地工作,至少在没有更改的情况下。
我的程序中有一堆检查器,我建模为 classes:检查 RAM 是否正常,检查磁盘是否正常,检查温度是否正常,等等。这些检查器有很多共同点,所以我用继承对它们进行建模:所有共同点都进入一个基础 class CheckerBase
,该基础 class 派生自具有检查器特定功能和依赖关系的专门 classes。
但是我经常读到组合应该比继承更受青睐,所以我想知道在 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();
}
编辑:已更新以包括如何使用跳棋的近似值。 A MasterChecker
定期运行所有单个检查程序。它有一个检查器列表并调用它们的 runTest()
成员函数——所有检查器都从它们的基础 class.
... composition should be preferred over inheritance
这意味着,你可以选择两者之一,但更喜欢合成。在这种情况下,MasterChecker
(正确地)组成了各种具体的检查器,正如您建议的那样。
单个跳棋 inherit/implement 抽象基础 class 的事实不是问题,因为您无法 组合 接口。这里别无选择,而且建议并没有说你永远不应该使用继承,即使组合不是替代方案。
您的建议实际警告的情况是:
class MasterChecker: public DiskChecker, public TemperatureChecker
其中滥用继承来聚合基础 class 子对象。
在你的情况下,由于初始化顺序和菱形继承的原因,这可能无论如何都不会很好地工作,至少在没有更改的情况下。