重构-打破依赖

时间:2018-10-05 20:25:06

标签: oop design-patterns dependency-injection dependencies refactoring

目前,我正在研究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();
        /* ... */
    }
}

如果这将是一个真实的应用程序,我不想破坏AlarmSensor之间的任何依赖关系。为此,我将创建以下构造函数

public Alarm() {
    this.sensor = new Sensor();
}

但是我的直觉说这是代码的味道。

如何处理现实应用程序中的此类依赖关系?

3 个答案:

答案 0 :(得分:2)

依赖倒置原则(DIP)指出:

  

高级模块不应依赖于低级模块。

但是,通过定义创建Sensor实现的默认构造函数,您违反了DIP,因为Sensor是一个低级模块,而Alarm(您的高级模块)需要一个DIP。依赖它:

public Alarm() {
    this.sensor = new Sensor();
}

如果需要让高级模块依赖于抽象(如您的其他构造函数所示),则无需添加默认构造函数。这样做只会将依赖项拖至低级模块。由于最终应用程序和测试都应使用重载的构造函数,因此默认构造函数没有任何意义,只会导致紧密耦合,从而违反了DIP。

这不是理论上的练习。在实际应用中应遵循DIP。一个经过精心设计的实际应用程序将应用SOLID原理,并使用“依赖注入”作为实现松耦合和DIP的方式。这样可以解耦模块,并允许在Composition Root中构成完整的对象图。

答案 1 :(得分:1)

为了不破坏AlarmSensor之间的任何依赖性,您需要:

  1. 采用Sensor使用的所有Alarm方法;
  2. 将它们放在一个(或多个)界面中;然后,
  3. 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容器已变得非常流行,尤其是在现代框架中。