说到依赖倒置原则,肯定很多人比较迷糊了,什么是依赖?为什么还要倒置?依赖倒置有啥有点?
下面我们从依赖倒置原则得定义,实现方式等角度具体来说下。
依赖倒置得原始定义是:
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 abstracts.
翻译过来的意思,包括以下三点:
1、高级模块不应依赖底层模块,两者都应依赖其抽象。
2、抽象不应该依赖其细节。
3、细节应该依赖其抽象。
高级模块和低层模块是什么意思呢?
一般我们的逻辑的实现都是由不可再分的原子逻辑组成,那么这个不可再分的原子逻辑就是低层模块,而由原子逻辑再组装的模块就是高级模块。
抽象很好理解,比较我们JAVA都是面向对象编程。抽象一般是指接口或者抽象类,两者都不能直接被实例化。而细节,就是其实现类。
那么我们现在再来看看依赖倒置的意思,他在JAVA上表现出来的意思就是:
1、模块间的依赖,通过抽象发生,实现类之间不能直接发生依赖关系,实现类之间的依赖关系是通过接口或者抽象类实现的。
2、接口或者抽象类不应该依赖实现类。
3、实现类要依赖接口或者抽象类。
编程时,我们常常会想我们为什么需要使用依赖倒置?依赖倒置有啥好处?
讲好处之前我们不妨先说说不使用依赖倒置的坏处。
我们构建一个驾驶员开车的场景:
如上图,我们可以看到,驾驶员开始得需要驾驶员(driver)类,和汽车类(car)。
Driver:
//驾驶员
public class Driver {
void driver(CarBenz car){
car.run();
}
}
Car:
//汽车类
public class CarBenz {
void run(){
System.out.println("开奔驰咯。。。");
}
}
开车:
import org.springframework.context.annotation.Bean;
public class Test {
public static void main(String[] args){
Driver driver = new Driver();
CarBenz benz = new CarBenz();
driver.driver(benz);
}
}
输出结果为:
开奔驰咯。。。
看了上面的代码,似乎我们不用依赖倒置也是可以开车,实现驾驶的功能。
但是,生活毕竟是生活,我总不能只开这一辆车吧?如果我想换车开,怎么办?是不是就得改写driver里的方法?如果我不定期增加呢?是不是driver会越扩越大?
而且,这还不是最头疼的,如此写法,driver高度依赖Car,我们正常研发的话,如果A程序员研发driver,B程序员研发Car。那么A程序员必须等到B程序员研发完毕才可以开始工作,而测试则更是要等到B和A都完成后才开始。一旦B有所改动,牵一发就会牵一发而动全身。这会是一个让人崩溃的事。
这就像流水线,必须等到上一个人完成后,下一个人才可以开始,这是万万不能忍受的。
那么有没有更好的办法呢?
答案是肯定有,接下来欢迎:依赖倒置原则正式入场
ICard(汽车类接口):
//汽车类接口
public interface ICard {
void run();
}
Benz:
public class Benz implements ICard {
@Override
public void run() {
System.out.println("开奔驰咯。。。");
}
}
BMW:
public class BMW implements ICard {
@Override
public void run() {
System.out.println("宝马启动啦。。。");
}
}
IDriver:
//司机接口
public interface IDriver {
void driver(ICard card);
}
DriverImpl:
//IDriver实现类
public class DriverImpl implements IDriver {
@Override
public void driver(ICard card) {
card.run();
}
}
上车吧,兄弟:
import org.springframework.context.annotation.Bean;
public class Test {
public static void main(String[] args){
IDriver driver = new DriverImpl();
ICard card = new Benz();
driver.driver(card);
}
}
这么一看,无论我们后来再添加什么车,只需要重新实现该车的实现类就可以了,对其他没有任何影响。
对象的依赖有三种传递方式:
1、构造函数传递依赖对象
IDriver:
//司机接口
public interface IDriver {
void driver();
}
DriverImpl:
//IDriver实现类
public class DriverImpl implements IDriver {
private ICard card;
public DriverImpl(ICard card){
this.card = card;
}
@Override
public void driver() {
this.card.run();
}
}
上车试试:
import org.springframework.context.annotation.Bean;
public class Test {
public static void main(String[] args){
IDriver driver = new DriverImpl(new BMW());
driver.driver();
}
}
2、setter传递依赖对象
IDriver:
//司机接口
public interface IDriver {
void setter(ICard card);
void driver();
}
DriverImpl:
//IDriver实现类
public class DriverImpl implements IDriver {
private ICard card;
@Override
public void setter(ICard card) {
this.card = card;
}
@Override
public void driver() {
this.card.run();
}
}
试试:
public class Test {
public static void main(String[] args){
IDriver driver = new DriverImpl();
driver.setter(new Benz());
driver.driver();
}
}
3、接口声明依赖对象
第二点采用的就是接口声明依赖对象,该方法也叫接口注入,这里就不再举例子了。