依赖倒置原则(Dependence Inversion Principle)

1 定义

      抽象不应该依赖于细节。细节应该依赖于抽象。依赖倒置原则主要体现在两个方面:

  • 高层模块不应该依赖于低层模块。二者都应该依赖于抽象。
  • 抽象不应该依赖于细节。细节应该依赖于抽象。

返回目录

2 如何实现

      现在考虑一个问题,当高层模块依赖于低层模块时意味着什么。高层模块包含了一个应用程序中的重要的策略选择和业务模型。正是这些高层模块才使得其所在的应用程序区别于其他。然而,如果这些高层模块依赖于低层模块,那么对低层模块的改动就会直接影响到高层模块,从而迫使它们依次做出改动。

2.1 问题: 违反DIP原则

      例如如下设计,Button依赖于Lamp,Lamp的改变会导致Button的改变,而且Button也只能适用于Lamp的开关方法,其他设备不支持,如扩展新的设备,Button类中需要添加新设备的属性,并有可能需要对应修改调用方法。
依赖倒置原则(Dependence Inversion Principle)_第1张图片
代码清单:

  1. Lamp.java
  2. Button.java
  3. DipDemo.java

1 Lamp.java

public class Lamp {
    public void turnOn() {
        System.out.println("开灯");
    }

    public void turnOff() {
        System.out.println("关灯");
    }
}

2 Button.java

public class Button {
    private Lamp lamp;

    public Button(Lamp lamp) {
        this.lamp = lamp;
    }

    public void poll() {
        while (true) {
            Date currentDate = new Date();
            SimpleDateFormat sd = new SimpleDateFormat("hh:mm");
            String currentDateStr = sd.format(currentDate);
            System.out.println(currentDateStr);
            // 晚上六点开灯,早上七点关灯
            if ("18:00".equals(currentDateStr)) {
                lamp.turnOn();
            } else if ("7:00".equals(currentDateStr)) {
                lamp.turnOff();
            }

            try {
                Thread.sleep(60000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

3 DipDemo.java

public class DipDemo {
    public static void main(String[] args) {
        Lamp lamp = new Lamp();
        Button button = new Button(lamp);

        button.poll();
    }
}

2.2 遵循DIP原则示例1

      为了改进上述设计,修改设计如下UML所示
依赖倒置原则(Dependence Inversion Principle)_第2张图片代码清单:

  1. SwitchableDevice.java
  2. Lamp.java
  3. Tv.java
  4. Button.java
  5. DipDemo1.java

1 SwitchableDevice.java

public interface SwitchableDevice {
    /**
     * 开启方法
     */
    void turnOn();

    /**
     * 关闭方法
     */
    void turnOff();
}

2 Lamp.java

public class Lamp implements SwitchableDevice{
    @Override
    public void turnOn() {
        System.out.println("台灯开灯");
    }

    @Override
    public void turnOff() {
        System.out.println("台灯关灯");
    }
}

3 Tv.java

public class Tv implements SwitchableDevice {
    @Override
    public void turnOn() {
        System.out.println("开电视");
    }

    @Override
    public void turnOff() {
        System.out.println("关电视");
    }
}

4 Button.java

public class Button {
    private SwitchableDevice device;

    public Button(SwitchableDevice device) {
        this.device = device;
    }

    public void poll() {
        device.turnOn();
        device.turnOff();
    }
}

5 DipDemo1.java

public class DipDemo1 {
    public static void main(String[] args) {
        SwitchableDevice lamp = new Lamp();
        SwitchableDevice tv = new Tv();
        // 创建新的可开关设备Button类无需改动
        Button button1 = new Button(lamp);
        Button button2 = new Button(tv);

        button1.poll();
        button2.poll();
    }
}

2.3 遵循DIP原则示例2

      这里举一个熔炉的例子,UML设计如下,将有可能改变的温度计和加热器以接口的方式传入regulate。无论温度计或者加热器的实现如何改变,regulate方法都不用做出调整。
依赖倒置原则(Dependence Inversion Principle)_第3张图片代码清单:

  1. AbstractAdjustableTemperatureDevice.java --抽象类
  2. Smelter.java
  3. ThermometerService.java --接口
  4. Thermometer.java
  5. HeaterService.java --接口
  6. Heater.java
  7. DipDemo2.java

1 AbstractAdjustableTemperatureDevice.java

public abstract class AbstractAdjustableTemperatureDevice {
    double temp;

    public abstract double getTemp();

    public abstract void setTemp(double temp);
}

2 Smelter.java

public class Smelter extends AbstractAdjustableTemperatureDevice {

    @Override
    public double getTemp() {
        return temp;
    }

    @Override
    public void setTemp(double temp) {
        this.temp = temp;
    }
}

3 ThermometerService.java

public interface ThermometerService {
    /**
     * 读取温度
     *
     * @return 返回当前温度
     */
    double read();
}

4 Thermometer.java

public class Thermometer implements ThermometerService {
    AbstractAdjustableTemperatureDevice device;

    Thermometer(AbstractAdjustableTemperatureDevice device) {
        this.device = device;
    }

    @Override
    public double read() {
        return device.getTemp();
    }

    public AbstractAdjustableTemperatureDevice getDevice() {
        return device;
    }

    public void setDevice(AbstractAdjustableTemperatureDevice device) {
        this.device = device;
    }
}

5 HeaterService.java

public interface HeaterService {
    /**
     * 增加温度
     */
    void addTemp();

    /**
     * 降低温度
     */
    void dropTemp();
}

6 Heater.java

public class Heater implements HeaterService {
    private AbstractAdjustableTemperatureDevice device;

    Heater(AbstractAdjustableTemperatureDevice device) {
        this.device = device;
    }

    @Override
    public void addTemp() {
        device.setTemp(device.getTemp() + 1);
    }

    @Override
    public void dropTemp() {
        device.setTemp(device.getTemp() - 1);
    }

    public AbstractAdjustableTemperatureDevice getDevice() {
        return device;
    }

    public void setDevice(AbstractAdjustableTemperatureDevice device) {
        this.device = device;
    }
}

7 DipDemo2.java

public class DipDemo2 {
    public static void main(String[] args) {
        AbstractAdjustableTemperatureDevice smelter = new Smelter();
        HeaterService heaterService = new Heater(smelter);
        ThermometerService thermometerService = new Thermometer(smelter);

        regulate(thermometerService, heaterService, 50, 60);
    }

    private static void regulate(ThermometerService thermometer, HeaterService heater, double minTemp, double maxTemp) {
        for (; ; ) {
            System.out.println("当前温度: " + thermometer.read());
            while (thermometer.read() < maxTemp) {
                heater.addTemp();
            }

            while (thermometer.read() > minTemp) {
                heater.dropTemp();
            }
        }
    }
}

3 结论

      使用传统的过程化程序设计所创建出来的依赖关系结构,策略是依赖于细节。面向对象的程序设计倒置了这种依赖关系,使得细节和策略都依赖于抽象,这样就可以减小由于具体实现细节的改动引起高层代码的变动。

返回目录

你可能感兴趣的:(设计原则,后端,设计原则,java)