涉及依赖注入时如何使用组合而不是继承?
Posted
技术标签:
【中文标题】涉及依赖注入时如何使用组合而不是继承?【英文标题】:How to use composition instead of inheritance when dependency injection is involved? 【发布时间】:2018-10-01 09:38:10 【问题描述】:我的程序中有一堆检查器,我将它们建模为类:检查 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()
成员函数——所有检查器都从它们的基类覆盖。
【问题讨论】:
这个 sn-p 对我来说没有多大意义:你如何从你的跳棋中提取信息? isOK() 方法在哪里? @YSC 这只是继承如何建模的一个示例。假设依赖项具有执行检查器类使用的功能的功能。或者我应该以某种方式改进我的 sn-p? 您打算使用类的方式对于提出基于组合的设计至关重要。 您的用法或继承在这里是正确的。你不能有抽象成员,所以无论如何你不能有成员CheckerBase
来作文。
在尝试组合事物时应该首选组合。您已经在MasterChecker
中执行此操作,因为您组合/聚合了各种检查器,而不是从它们派生。实现抽象接口不是组合,继承就可以了。
【参考方案1】:
...组合应该优先于继承
这意味着,你可以选择其中一个,更喜欢组合。在这种情况下,MasterChecker
(正确)按照您的建议组成了各种具体的检查器。
各个检查器继承/实现抽象基类这一事实不是问题,因为您不能组合接口。这里别无选择,建议并没有说即使组合不是替代方案,也不应该使用继承。
您的建议实际上警告的情况是:
class MasterChecker: public DiskChecker, public TemperatureChecker
滥用继承来聚合基类子对象。
在您的情况下,由于初始化顺序和菱形继承的原因,至少在没有更改的情况下,这可能不会很好地工作。
【讨论】:
以上是关于涉及依赖注入时如何使用组合而不是继承?的主要内容,如果未能解决你的问题,请参考以下文章