C++ C++;系统组件的依赖倒置

C++ C++;系统组件的依赖倒置,c++,solid-principles,C++,Solid Principles,到目前为止,我对该原则的理解相当肤浅——“高级模块不应该依赖于低级模块,所有模块都应该依赖于接口。” 这一点在许多书籍中都以示例的形式出现,其中负责启用/禁用Lamp的Button类不应依赖于它。按钮应提供Lamp实现的接口,而不是使用混凝土灯。根据Martin的一本书,接口应该由高级类/模块公开,并由低级模块实现。 正如我完全可以想象的那样,在一个库中使用这个原则,但是将其应用到更大的系统组件似乎很麻烦。让我们考虑以下错误设计的例子: ----------Library for group o

到目前为止,我对该原则的理解相当肤浅——“高级模块不应该依赖于低级模块,所有模块都应该依赖于接口。” 这一点在许多书籍中都以示例的形式出现,其中负责启用/禁用Lamp的Button类不应依赖于它。按钮应提供Lamp实现的接口,而不是使用混凝土灯。根据Martin的一本书,接口应该由高级类/模块公开,并由低级模块实现。 正如我完全可以想象的那样,在一个库中使用这个原则,但是将其应用到更大的系统组件似乎很麻烦。让我们考虑以下错误设计的例子:

----------Library for group of sensors for certain Vendor, let's call it 'sx' that exposes bunch of header files and dynamically linked library:

// SX4000.hpp
class SX4000{
  public:
    uint32_t getTemperature() const;
    uint32_t getBatteryLevel() const;
};

// libsx.so

----------Application using the sensor
// Display.cpp
#include "SX4000.hpp"

void DisplayTemperatureSensorDetails(const SX4000& sensor)
{
    auto temperature = sensor.GetTemperature();
    auto batteryLevel = sensor.GetBatteryLevel();
    // Logic sending values to the screen
    // ...
}
然后,采用建议方法的设计应为:

// Application using the sensor
// Measurable.hpp
class Measurable
{
  public:
    virtual uint32_t GetTemperature() const = 0;
    virtual uint32_t GetBatteryLevel() const = 0;
};


// Display.cpp
void DisplayTemperatureSensorDetails(const Measurable& sensor)
{
    auto temperature = sensor.GetTemperature();
    auto batteryLevel = sensor.GetBatteryLevel();
    // Logic sending values to the screen
    // ...
}

// Sensor library:
// SX4000.hpp
class SX4000 : public Measurable{
  public:
    uint32_t getTemperature() const;
    uint32_t getBatteryLevel() const;
};
// libsx.so
然而,这似乎违反了面向对象编程的基本规则之一。如果有低级库,为什么要将其调整到使用位置?如果系统的其他组件必须使用传感器库,该怎么办?这是否意味着每次使用时都应该向传感器库添加新接口? 我认为有两种方法可以解决这个问题:

  • 传感器库公开可测量的接口而不是应用程序
  • 应用程序创建一个实现可测量的适配器/包装器类,并在入口点注入
哪种解决方案更好,还有其他选择吗

编辑。
我只是想澄清一些我可能用错误的方式提出的观点。总的来说,我完全理解多态性的所有好处,我不怀疑这种方法。我唯一关心的是在具有相当成熟的接口的库之间反转依赖关系。在大型系统中,很有可能出现这样的情况,即所有低级库中有相当一部分已经建立了良好的接口。然后根据这个规则,我在上面写一个组件,向服务公开一个接口。然后,我必须用一个抽象来更新这种低层库的良好接口。我认为,在这种情况下,最好的方法是坚持使用低层库的良好接口,并在高层库中放置适配器。这种方法的利弊是什么?还有其他选择吗?

我认为您需要首先确定
SX4000
是否是唯一的
可测量的对象

例如,如果您有另一个单元,比如说
SX5000
,它也是一个
可测量的
。当您有
可测量
界面时,如果您有一个接受
可测量
的函数,它可以同时接受
SX4000
SX5000

更重要的是,在编写该函数时,您可能不知道还会有另一个
可测量的
对象。通过创建一个接口,您的函数将接受一个可测量的对象,这样函数就可以自动接受新的对象类型,而无需更改该函数的任何代码


另一方面,
SX4000
可能不仅仅是一个可测量的对象。比如说,它也可能是一个
开关
对象。通过创建
SX4000
继承
可测量的
开关
,您可以轻松地从这两个界面获取信息。

一个问题。。。
SX4000
a
是可测量的对象(是一种关系),还是它有温度计和电池(有关系)?如果是第一个,那么您可能没问题,但我猜它包含其他组件,因此应该将这些完整的对象作为组件包括在内,因此隐藏了这些组件如何在更高的
SX4000
类中工作的内部。您的第一个示例的传感器类似乎更好。感谢您的回复。出于这个特殊的考虑,我假设这是纯粹的“是”关系。关键关注点是,解决在一个库中具有与更高层服务的接口(根据依赖关系倒置)并在另一个库中根据概念实现该接口的问题的最佳方法是什么。看来,至少在高级库中使用适配器是不可避免的。