依赖倒置原则的澄清

Clarification on the Dependency Inversion Principle

请原谅我在软件工程上交叉发帖,不知道有人不赞成。

我得到的答案正是我想要的,对于那些好奇的人:https://softwareengineering.stackexchange.com/a/347143/269571


原题

我正在读 "Agile Software Development, Principles, Patterns, and Practices" 的书 Robert C. Martin

当他谈到依赖倒置原则时,他给出了以下违反 DIP 的示例:

这对我来说似乎很清楚,因为更高级别的对象 Button 依赖于较低级别的对象 Lamp

他自带的解决方案是:

他创建了一个接口,这样Button就不再依赖对象Lamp

这个理论对我来说似乎很清楚,但是我无法在实际项目中使用这个原理。

如果我的问题有任何不清楚的地方,请告诉我,我很乐意为您澄清。

他是说按钮控制的内容应该比 lamp 更通用。如果按钮可以控制的每种类型的事物都有按钮 classes,那么您可能会得到很多按钮 classes。

在第一个示例中,描述了 lamp 上的按钮。它本质上是以lamp为起点,将其分解成组件。

在第二个例子中,他正在划分部分并更笼统地看一个按钮。

Who is going to determine what classes (that implement SwitchableDevice) need to be called?

按钮和界面之间必须有一个 link。

Who tells Button what devices he need to turn on/off?

Button class 需要实施一种机制来告知它连接到的设备。

How do you tell an object that uses something abstract which concrete things it needs to use? (Please correct me if this question is completely wrong).

因为派生自抽象接口的对象必须完全实现该接口。 Lamp 对象必须在某处定义 TurnOn 和 TurnOff 方法..

依赖注入的全部要点(至少在我看来是这样)是 Button 不需要知道它正在切换的具体 SwitchableDevice 是什么。

抽象接口可能如下所示:

struct SwitchableDevice {
    virtual void switchOn() = 0;
    virtual void switchOff() = 0;
};

按钮可以这样实现:

struct Button {
    SwitchableDevice& dev;
    bool state = false;
    Button(SwitchableDevice& d) : dev(d) {}
    void buttonPress(){
        if (state) { dev.switchOff(); }
        else       { dev.switchOn();  }
        state = !state;
    }
};

对于按钮,仅此而已!没有人需要告诉按钮 SwitchableDevice 的具体实现是什么,换句话说: ButtonSwitchableDevice 的实现是解耦的。

Lamp 的可能实现如下所示:

struct Lamp : SwitchableDevice {
    void switchOn(){std::cout << "shine bright" << std::endl;}
    void switchOff(){std::cout << "i am not afraid of the dark" << std::endl;}
};

可以这样使用:

int main(){
    Lamp lamp;
    Button button(lamp);
    button.buttonPress();
    button.buttonPress();
}

希望对您有所帮助...

好处是现在我们可以单独更改 ButtonLamp 的实现,而无需更改其他部分的任何内容。例如 ButtonForManyDevices 可能如下所示:

struct ButtonForManyDevices {
    std::vector<SwitchableDevice*> devs;
    bool state = false;
    Button(std::vector<SwitchableDevice*> d) : devs(d) {}
    void buttonPress(){
        if (state) for (auto d: devs) { d.switchOff(); }
        else       for (auto d: devs) { d.switchOn();  }
        state = !state;
    }
};

同样,您可以完全更改 Lamp 的行为(当然在 SwitchableDevice 的限制内,而无需更改按钮上的任何内容。相同的 ButtonForManyDevices 甚至可以用于切换 LampVaccumCleanerMicroWaveOven.