请原谅我在软件工程上的交叉发布,不知道它是不受欢迎的。
我得到的答案正是我所寻找的,对于那些好奇的人:https://softwareengineering.stackexchange.com/a/347143/269571
原始问题
我正在阅读 Robert C. Martin 的"敏捷软件开发,原则,模式和实践" 这本书。
当他谈到依赖性倒置原则时,他给出了以下DIP违规示例:
这对我来说非常清楚,因为较高级别的对象Button
依赖于较低级别的对象Lamp
。
他带来的解决方案是:
他创建了一个界面,以便Button
不再依赖于对象Lamp
。
这个理论对我来说似乎很清楚,但是我无法在真实的项目中使用这个原理。
谁将确定需要调用哪些类(实现SwitchableDevice
)?
谁告诉Button
他需要打开/关闭哪些设备?
如何告诉对象使用 abstract 哪些具体需要使用的东西? (如果这个问题完全错误,请纠正我)
如果对我的问题不清楚,请告诉我,我很乐意为你澄清事情。
答案 0 :(得分:3)
依赖注入的全部要点(至少我理解)是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
的具体实现是什么,换句话说:Button
和SwitchableDevice
的实现是分离的。
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();
}
希望有帮助...
好处是,现在我们可以单独更改Button
和Lamp
的实现,而无需在其他部分更改任何内容。例如,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
切换Lamp
,VaccumCleaner
和MicroWaveOven
。
答案 1 :(得分:0)
他说按钮控制的东西应该比一盏灯更普遍。如果你有一个按钮可以控制的每种类型的按钮类,那么你最终可以使用很多按钮类。
在第一个例子中,一个是描述灯上的按钮。它主要是以灯为起点并将其分成组件。
在第二个例子中,他正在分割部件并更普遍地查看按钮。
谁将确定需要调用哪些类(实现SwitchableDevice)?
按钮和界面之间必须有一个链接。
谁告诉Button他需要打开/关闭哪些设备?
Button类需要实现一种机制来告诉它连接到哪个设备。
你如何告诉一个使用抽象的对象需要使用哪些具体的东西? (如果这个问题完全错误,请纠正我。)
因为从抽象接口派生的对象必须完全实现该接口。 Lamp对象必须在某处定义TurnOn和TurnOff方法..