依赖倒置原则
概念:英文全称是Dependence Inversion Principle(DIP),依赖倒置原则指代了一种特定的解耦形式,使得高层次(指调用端)的模块不依赖于低层次(具体实现类)的模块的实现细节的目的,依赖模块被颠倒了。该原则有以下几个关键点:(1)高层模块不应该依赖低层模块,两者都应该依赖其抽象;(2)抽象不应该依赖细节;(3)细节不应该依赖抽象。java语言中的表现就是:模块间的依赖通过抽象发生,实现类之间不发生直接的依赖关系,其依赖关系是通过接口或抽象类产生的。简单概括就是面向接口编程,或者说是面向抽象编程。举例如下:
先看一个反例:
public class Driver {
public void drive(Benz benz){
benz.run();
}
}
public class Benz {
public void run(){
System.out.println("奔驰汽车开始启动...");
}
}
public class Client {
public static void main(String[] args) {
Driver lisi = new Driver();
Benz benz = new Benz();
lisi.drive(benz);
}
}
分析:程序能够正常运行,如果这时候lisi又要开宝马车,就会出现麻烦,不能开。先把宝马产生出来,如下:
public class BMW {
public void run(){
System.out.println("宝马汽车启动...");
}
}
原因:司机类与奔驰车类之间是紧耦合的关系,导致系统的可维护性降低,可读性降低,增加一个车类就需要修改司机类,这没有体现稳定性,而是易变性。被依赖者的变更需要让依赖者来承担修改的成本,这样的依赖关系很糟!此时,就需要引入依赖倒置原则。具体如下:
public interface IDriver {
public void drive(ICar car);
}
public interface ICar {
public void run();
}
public class Driver implements IDriver{
public void drive(ICar car) {
car.run();
}
}
public class Benz implements ICar{
public void run(){
System.out.println("奔驰汽车开始启动...");
}
}
public class BMW implements ICar{
public void run(){
System.out.println("宝马汽车启动...");
}
}
public class Client {
public static void main(String[] args) {
IDriver lisi = new Driver();
ICar benz = new Benz();
lisi.drive(benz);
}
}
分析:在业务场景中,贯彻“抽象不应该依赖细节”,也就是我们认为抽象(ICar接口)不依赖BMW和Benz两个实现类(细节),因此高层次的模块中应用都是抽象,如上面Client的实现过程。
也许你会问,在这个高层模块中也调用到了底层模块,比如new Driver()和new Benz()等,怎么解释?确实如此,lisi的表面类型是IDriver,是抽象的,非实体化的,在其后的所有操作中,lisi都是以IDriver类型进行操作,它屏蔽了细节对抽象的影响。如果lisi要开宝马,只需要修改业务场景类就可以,把“变更”引起的风险扩散降到最小。
注意:在java中,只要定义变量就必然要有类型,一个变量可以有两种类型:表面类型和实际类型,表面类型是在定义的时候赋予的类型,实际类型是对象的类型,如lisi的表面类型是IDriver,实际类型是Driver.
依赖的三种写法
1 构造函数传递依赖对象
public interface IDriver {
public void drive( );
}
public class Driver implements IDriver{
private ICar car;
public Driver(ICar car) {
this.car=car;
}
public void drive( ) {
this.car.run();
}
}
2 Setter方法传递依赖对象
public interface IDriver {
public void setCar(ICar car);
public void drive();
}
public class Driver implements IDriver{
private ICar car;
public void setCar(ICar car) {
this.car=car;
}
public void drive() {
this.car.run();
}
}
3 接口声明依赖对象
总结:依赖倒置原则的本质就是通过抽象(接口或抽象类)使各个类或模块的实现彼此独立,不互相影响,实现模块间的松耦合。
总结:依赖倒置原则的本质就是通过抽象(接口或抽象类)使各个类或模块的实现彼此独立,不互相影响,实现模块间的松耦合。