抽象不应该依赖于细节。细节应该依赖于抽象。依赖倒置原则主要体现在两个方面:
返回目录
现在考虑一个问题,当高层模块依赖于低层模块时意味着什么。高层模块包含了一个应用程序中的重要的策略选择和业务模型。正是这些高层模块才使得其所在的应用程序区别于其他。然而,如果这些高层模块依赖于低层模块,那么对低层模块的改动就会直接影响到高层模块,从而迫使它们依次做出改动。
例如如下设计,Button依赖于Lamp,Lamp的改变会导致Button的改变,而且Button也只能适用于Lamp的开关方法,其他设备不支持,如扩展新的设备,Button类中需要添加新设备的属性,并有可能需要对应修改调用方法。
代码清单:
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();
}
}
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();
}
}
这里举一个熔炉的例子,UML设计如下,将有可能改变的温度计和加热器以接口的方式传入regulate。无论温度计或者加热器的实现如何改变,regulate方法都不用做出调整。
代码清单:
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();
}
}
}
}
使用传统的过程化程序设计所创建出来的依赖关系结构,策略是依赖于细节。面向对象的程序设计倒置了这种依赖关系,使得细节和策略都依赖于抽象,这样就可以减小由于具体实现细节的改动引起高层代码的变动。
返回目录