前言:
最近在看书的时候看到了一个很熟悉的名词,依赖倒置原则,这个对于Java开发人员来说应该都很熟悉,但是有多少人真的搞懂了这个名词的含义?网上大概搜索了一下基本上都是只有名词的定义,而不是真正的去解释这个含义,所以我决定写一篇文章分析一下这个名词的含义。如果哪里写的不对也欢迎指正和批评。
正文:
一:依赖倒置原则的定义
1.High-level modules should not depend onlow-level modules. Both should depend on abstractions.
2.Abstractions should not depend upondetails. Details should depend upon abstractions.
这两句话的意思是:1:高层模块不应依赖于低层模块。两者都应该依赖于抽象。2:抽象不应该依赖于细节,细节应该依赖抽象。
其实这个定义给的确实比较难以理解,要吃透这个含义还要先看一下代码。
二:不使用依赖倒置原则的设计有什么问题?
拿比较通用的驾驶员和车来做代码举例
下面的这个是建一个有C1驾照的驾驶员的类,司机可以驾驶兰博基尼汽车。(最近在考驾照有点着魔)
public class C1Driver {
public String getDriverLicenseType(){
return "C1";
}
public void drive(Lamborghini lamborghini){
lamborghini.run();
}
}
下面的是建一个兰博基尼汽车类
public class Lamborghini {
public void run() {
System.out.println("兰博基尼开始运行...");
}
}
运行
public class Client {
public static void main(String[] args) {
C1Driver zhangsan = new C1Driver();
zhangsan.drive(new Lamborghini());
}
}
上面的这段代码对于司机张三驾驶他的兰博基尼一点问题都没有,那么当张三想买辆宝马开着玩玩怎么办呢?
对于上面的实现来说,就只能从高层模块(调用端)开始改起。C1驾驶员类改为
public class C1Driver {
public String getDriverLicenseType(){
return "C1";
}
public void drive(Lamborghini lamborghini){
lamborghini.run();
}
public void drive(BMW bmw){
bmw.run();
}
}
(底层模块)被调用端新建一个宝马的汽车类
public class BMW {
public void run() {
System.out.println("宝马开始运行...");
}
}
public static void main(String[] args) {
C1Driver zhangsan = new C1Driver();
zhangsan.drive(new BMW());
}
这样虽然实现了张三驾驶宝马车的愿望,那么问题又来了,李四也想开宝马,但是他只有C2的驾照,怎么办呢,只能继续新建一个C2驾照的司机类,然后调用BMW的汽车类,这里就不做代码展示了,通过上面这段代码可以看出,这种实现方式极其的糟糕和不稳定,稍有变动全局都要改一遍,那么针对这种问题架构设计上注意的原则是什么呢?那就是依赖倒置原则,这也是面向接口编程的精髓和基础原则。
三:何为依赖倒置?
上面讲了依赖倒置的定义和不遵守依赖倒置原则产生的问题,那么到底依赖倒置是什么意思?
用上面的代码做类图进行分析
如果所示,C1驾驶员类的实现依赖于汽车类兰博基尼的实现,这个就是依赖正置,就是实现依赖实现,按照直接思考的顺序来说,我有C1驾照我也有兰博基尼我就可以开。
按照依赖倒置的定义来分析
如图可见,C1Driver和C2Driver都共同实现了一个IDriver的抽象,同样两个汽车类也都共同依赖了一个ICar汽车类的抽象,在这种设计下,当张三又想开奔驰了,新建一个奔驰类即可。李四也想开车,新建一个C2驾照的实现类即可。
按照依赖倒置原则设计的代码如下:
司机类:
public interface IDriver {
String getDriverLicenseType();
void drive(ICar car);
}
public class C1Driver implements IDriver{
public String getDriverLicenseType(){
return "C1";
}
public void drive(ICar iCar){
iCar.run();
}
}
public class C2Driver implements IDriver{
public String getDriverLicenseType(){
return "C2";
}
public void drive(ICar iCar){
iCar.run();
}
}
汽车类:
public interface ICar {
void run();
}
public class BMW implements ICar{
public void run() {
System.out.println("宝马开始运行...");
}
}
public class Lamborghini implements ICar{
public void run() {
System.out.println("兰博基尼开始运行...");
}
}
运行
public class Client {
public static void main(String[] args) {
IDriver zhangsan = new C1Driver();
zhangsan.drive(new BMW());
zhangsan.drive(new Lamborghini());
IDriver lisi = new C2Driver();
lisi.drive(new BMW());
lisi.drive(new Lamborghini());
}
}
根据上面的类图和实现可以看出,依赖倒置实际的含义就是原本由实现类和实现类之间的依赖,倒置为相关的实现类所继承的父类或者所实现的接口之间的依赖。
通过依赖倒置,可以有效的降低类与类之间的耦合度,提高系统的稳定性,提高代码的可维护性,同时也降低了因为需求变更带来的修改代码的风险。