原始定义
High level modules should not depend upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions.
三层含义:
- 高层模块不应该依赖底层模块,两者都依赖其抽象。
- 抽象不依赖细节。
- 细节应该依赖于抽象。
【注解】在Java语言中,抽象就是指接口或抽象类,两者都是不能直接被实例化的。细节就是具体的实现类,实现类实现了接口或继承了抽象类,其特点是可以直接被实例化。依赖倒置原则在Java语言中的表现是:
- 模块间的依赖通过抽象产生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生。
- 接口或抽象类不依赖于实现类,实现类依赖于接口或抽象类。
依赖倒置原则更加精确的定义就是“面向接口的编程”——OOD(Object-Oriented Design)的精髓之一。其是JavaBean、EJB和COM等组件设计模型背后的基本原则。
示例
在现实生活中,司机只要会开车,就可以开奔驰车,也可以开宝马车。因此司机不依赖于奔驰或宝马车,而是通过如下图所示的接口,使他们之间的依赖关系倒置。
司机接口只是一个抽象的概念,是对司机这类事物的抽象,只要是司机都有一个共同的行为——开车,因此,在IDriver接口中使用drive()方法进行抽象。接口IDriver源代码如下:
public interface IDriver {
//司机都会驾驶汽车
public void drive(ICar car);
}
司机类Driver实现IDriver接口,Driver源代码如下:
public class Driver implements IDriver {
//司机的主要职责是驾驶
@Override
public void drive(ICar car) {
car.run();
}
}
汽车接口ICar的源代码如下:
public interface ICar {
//汽车都能跑
public void run();
}
奔驰汽车类Benz实现ICar接口,源代码如下:
public class Benz implements ICar {
@Override
public void run() {
System.out.println("奔驰汽车在行使...");
}
}
宝马汽车类BMW也实现ICar接口,源代码如下:
public class BMW implements ICar {
@Override
public void run() {
System.out.print("宝马汽车在行使...");
}
}
测试类Test,源代码如下:
public class Test {
public static void main(String[] args) {
//实例化一个司机
IDriver tom = new Driver();
//实例化一辆宝马车
ICar car = new BMW();
//tom开车
tom.drive(car);
}
}
【注解】这程序看起来就非常的6了,虽然规模很小,当司机tom想开奔驰车时,只需将“new BMW()”改为“new Benz()”即可,更改会引起的风险很小。这样的原则果然乃出自大牛之作,相比大家也迫不及待地想学习使用了,不过先别急,我们还需要弄懂一件事,那就是它适用的场景,有什么优点?
依赖倒置原则在小型项目中很难体现出来。在大中型项目中,采用依赖倒置原则具有非常多的优点,特别是它能规避一些非技术原因引起的问题。项目越大,需求变化的概率也越大,采用依赖倒置原则可以使接口或抽象类对实现类进行约束,从而减少需求变化引起的工作量剧增的情况。人员的变动在大中型项目中也是时常存在的,如果程序设计优良、代码结构清晰时,人员变化对项目的影响就会很小。同时,它是实现开闭原则的重要途径,依赖倒置原则没有实现,就不能实现对扩展的开放,对修改关闭。
在项目中使用这个原则需要遵循以下几个规则:
- 每个类应该都具有接口或抽象类,或者同时具备抽象类和接口。
- 变量的表面类型(表面类型是在定义的时候赋予的类型,实际类型是创建时对象的类型,例如tom表面类型是IDriver,实际类型是Driver)尽量是接口或者是抽象类。
- 任何类都不应该从具体类派生。
- 尽量不要重写基类的方法。如果基类是一个抽象类,并且其方法已经实现了,子类尽量不要重写。
- 结合里氏替换原则。里氏替换原则指出父类出现的地方子类就可以出现,结合依赖倒置原则可以得出一个通俗的规则:接口负责定义抽象方法,并且声明与其他对象的依赖关系;抽象类负责公共构造部分的实现,实现类准确地实现业务逻辑,同时在适当的时候对父类进行细化。