本专栏内容参考自:咕泡学院Tom老师的《Spring5核心原理与30个类手写实战》,仅作个人学习记录使用,如有侵权,联系速删
依赖倒置原则(Dependence Inversion Principle,DIP)是只设计代码结构时,高层代码不应依赖低层代码,二者都应依赖其抽象。
抽象不应该依赖细节,细节应该依赖抽象
通过以来倒置,可以减少类与类之间的耦合性,提高系统的稳定性,提高代码的可读性和可维护性,并且能够降低修改程序所造成的风险。接下来看一个案例,还是以商品Goods为例,先来创建一个类,购买者Buyer
public class Buyer {
public void buyBook(){
System.out.println("买书");
}
public void buyFruit(){
System.out.println("买水果");
}
}
调用一下:
public class Main {
public static void main(String[] args) {
Buyer buyer = new Buyer();
buyer.buyBook();
buyer.buyFruit();
}
}
我有钱,我要买更多种类的东西,怎么办,这就是业务拓展了,如果我们选择从底层到高层依次修改代码的方式:在购买者Buyer类中增加buyOther()方法,那么,在高层也要追加调用。
如此一来,系统发布以后,实际上是非常不稳定的,在修改代码的同时也会带来意想不到的危险。
怎么改进呢,我们不妨创建一个购买者的接口,IBuyer(),然后在里面添加购买方法,分别去实现,这样,每样东西都有了它自己的专属类。
创建一个购买者接口:
public interface IBuyer {
void buy();
}
然后再编写买书买水果的类
public class bookBuyer implements IBuyer {
@Override
public void buy() {
System.out.println("买书");
}
}
public class fruitBuyer implements IBuyer {
@Override
public void buy() {
System.out.println("买水果");
}
}
再将Buyer类修改下:
public class Buyer {
// public void buyBook(){
// System.out.println("买书");
// }
// public void buyFruit(){
// System.out.println("买水果");
// }
public void buy(IBuyer buyer){
buyer.buy();
}
}
然后再调用:
public class Main {
public static void main(String[] args) {
Buyer buyer = new Buyer();
// buyer.buyBook();
// buyer.buyFruit();
buyer.buy(new bookBuyer());
buyer.buy(new fruitBuyer());
}
}
这时候再来看我们的业务,不管我想买什么,也不管我想买多少种类,是不是都不会对底层代码产生影响,只需要添加我们所需要的就可以了,不会改变已有的,也不会对已有的产生影响。
由于示例的业务比较简单,所以大家可能会感到反而麻烦了,如果我们的业务复杂一点,每一样商品都有不同的活动,甚至每一个商品的不同活动还有细分,那么我们再看这种方式,是不是就比最开始的方式要好上无数倍?
实际上这种是大家非常熟悉的方式:
依赖注入
另外注入的方式还有构造器注入和Setter方式,我们先来看构造器注入方式:
修改购买者类Buyer的构造方法:
public class Buyer {
// public void buyBook(){
// System.out.println("买书");
// }
// public void buyFruit(){
// System.out.println("买水果");
// }
private IBuyer buyer;
//构造器注入
public Buyer(IBuyer buyer){
this.buyer = buyer;
}
public void buy(){
buyer.buy();
}
}
看调用:
public class Main {
public static void main(String[] args) {
// Buyer buyer = new Buyer();
// buyer.buyBook();
// buyer.buyFruit();
// buyer.buy(new bookBuyer());
// buyer.buy(new fruitBuyer());
Buyer bookBuyer = new Buyer(new bookBuyer());
Buyer fruitBuyer = new Buyer(new fruitBuyer());
bookBuyer.buy();
fruitBuyer.buy();
}
}
还有Setter注入方式:
修改购买这类:
public class Buyer {
// public void buyBook(){
// System.out.println("买书");
// }
// public void buyFruit(){
// System.out.println("买水果");
// }
private IBuyer buyer;
//setter注入方式
public void setBuyer(IBuyer buyer) {
this.buyer = buyer;
}
public void buy(){
buyer.buy();
}
}
调用:
public class Main {
public static void main(String[] args) {
// Buyer buyer = new Buyer();
// buyer.buyBook();
// buyer.buyFruit();
// buyer.buy(new bookBuyer());
// buyer.buy(new fruitBuyer());
Buyer buyer = new Buyer();
buyer.setBuyer(new bookBuyer());
buyer.buy();
buyer.setBuyer(new fruitBuyer());
buyer.buy();
}
}
以抽象为基准比以细节为基准搭建起来的架构要稳健的多,因此在拿到需求以后,要面向接口编程,先顶层再细节的设计代码结构,尽量让新添加的业务不会对已有的业务产生影响
。