目前,我正在研究Racing-Car-Katas。目标是重构大量的代码,使其遵循基本原则。
我尝试添加Dependency Inversion Principle。在哪里可以通过构造函数传递依赖项。
在类Alarm
中是依赖项Sensor
,它生成一个psiPressureValue
。
public class Alarm {
Sensor sensor = new Sensor();
public void check()
{
double psiPressureValue = sensor.popNextPressurePsiValue();
/* ... */
}
}
public class Alarm {
Sensor sensor;
public Alarm() {
this.sensor = new Sensor();
}
public Alarm(ISensor sensor) {
this.sensor = sensor;
}
public void check()
{
double psiPressureValue = sensor.popNextPressurePsiValue();
/* ... */
}
}
如果这将是一个真实的应用程序,我不想破坏Alarm
和Sensor
之间的任何依赖关系。为此,我将创建以下构造函数
public Alarm() {
this.sensor = new Sensor();
}
但是我的直觉说这是代码的味道。
如何处理现实应用程序中的此类依赖关系?
答案 0 :(得分:2)
依赖倒置原则(DIP)指出:
高级模块不应依赖于低级模块。
但是,通过定义创建Sensor
实现的默认构造函数,您违反了DIP,因为Sensor
是一个低级模块,而Alarm(您的高级模块)需要一个DIP。依赖它:
public Alarm() {
this.sensor = new Sensor();
}
如果需要让高级模块依赖于抽象(如您的其他构造函数所示),则无需添加默认构造函数。这样做只会将依赖项拖至低级模块。由于最终应用程序和测试都应使用重载的构造函数,因此默认构造函数没有任何意义,只会导致紧密耦合,从而违反了DIP。
这不是理论上的练习。在实际应用中应遵循DIP。一个经过精心设计的实际应用程序将应用SOLID原理,并使用“依赖注入”作为实现松耦合和DIP的方式。这样可以解耦模块,并允许在Composition Root中构成完整的对象图。
答案 1 :(得分:1)
为了不破坏Alarm
和Sensor
之间的任何依赖性,您需要:
Sensor
使用的所有Alarm
方法; Sensor
实现了这些接口。第二种构造方法不是保护依赖项的正确方法。通过创建Sensor
类的对象,这违反了DIP。
相反,将验证代码添加到其他构造函数中,并确保Alarm
始终通过Sensor
使用有效的ISensor
(而不创建{{ 1}})。
答案 2 :(得分:0)
好的,您想破坏警报和传感器之间的直接耦合。
您提出的解决方案显示了两个构造函数,一个构造函数注入Sensor对象(在外部创建),另一个直接创建Sensor对象。您应该删除:
public Alarm() {
this.sensor = new Sensor();
}
因为不需要它,并且因为它延长了SOLID所要避免的紧密耦合。这样做不会破坏依赖性。
广泛地,用于创建依赖项的最常见选项如下。 创建相关对象:
1)直接由Client对象(紧密耦合)
2)由客户端使用客户端的工厂方法间接
3)由客户使用注入到客户端的 Abstract Factory 对象间接进行(工厂注入为DI)。您可以注入不同的工厂。
4)从外部手动注入到客户端(DI)
5)通过DI容器从外部注入到客户端(DI)
DI 的关键点在于,客户端不控制其依赖项的创建方式。这就是术语“ 控制反转”的说明。
但是,客户端保留使用“ 工厂方法”,“ 抽象工厂”的控制权。
近年来,DI容器已变得非常流行,尤其是在现代框架中。