前言 上一節我們說了介面隔離原則,就是讓介面的職責最小化。這樣對維護代碼簡單,調用方法也清晰。 這節我們來研究依賴倒置原則。這個原則我認為是特別特別重要的。在很多地方我們能看到。比如Dubbo中使用到的SPI等等。 基本介紹 什麼是依賴倒置原則? 我們可以將其分為兩點: 1) 高層模塊不應該依賴低層 ...
前言
上一節我們說了介面隔離原則,就是讓介面的職責最小化。這樣對維護代碼簡單,調用方法也清晰。
這節我們來研究依賴倒置原則。這個原則我認為是特別特別重要的。在很多地方我們能看到。比如Dubbo中使用到的SPI等等。
基本介紹
什麼是依賴倒置原則?
我們可以將其分為兩點:
1) 高層模塊不應該依賴低層模塊,二者都應該依賴其抽象
2) 抽象不應該依賴細節,細節應該依賴抽象
其實總結下來,依賴倒轉(倒置)的中心思想是面向介面編程
相對於細節的多變性,抽象的東西要穩定的多。
我們以抽象的介面為基礎來架構我們的項目比以細節為基礎的架構要穩定的多。
在java中,抽象指的是介面或抽象類,細節就是具體的實現類
使用介面或抽象類的目的是制定好規範,而不涉及任何具體的操作,把展現細節的任務交給他們的實現類去完成
案例
上面乾巴巴的文字需要多讀幾遍,然後再來看案例。
反例
首先,我們來說明一個反例,如果沒有使用依賴倒置原則會有什麼問題?
public class Audi {
public void run(){
System.out.println("開奧迪上班");
}
}
class Person{
public void goToWork(Audi car){
car.run();
}
public static void main(String args[]){
Person person = new Person();
Audi audi = new Audi();
person.goToWork(audi);
}
}
上訴代碼輸出結果為:
開奧迪上班
大家思考一下這樣的設計有沒有問題?
很明顯,如果這個人換了一輛車,我們是不是需要改Person類的源代碼??
我們在Person這個類中實現了goToWork() 上班這個方法。並且傳入了奧迪車這個對象。
如果我們想換一輛車,只能修改goToWork()這個方法,傳入你想開的車。
這樣設計出來的代碼很不靈活。
我們來看看正確的做法是什麼。
使用依賴倒置原則
public class Audi {
public void run(){
System.out.println("開奧迪上班");
}
}
public class Jili {
public void run(){
System.out.println("開國產神車吉利上班");
}
}
public interface Car {
void run();
}
class Person{
public void goToWork(Car car){
car.run();
}
public static void main(String args[]){
Person person = new Person();
Audi audi = new Audi();
Jili jili = new Jili();
person.goToWork(audi);
person.goToWork(jili);
}
}
上訴代碼輸出結果
開奧迪上班
開國產神車吉利上班
不知道大家能不能從中看出實現依賴倒置原則的優勢?
沒錯,這下我們想開什麼車就開什麼車,不需要再去修改源代碼了。
這下我們再來理解之前的話,就清晰很多了。
相對於細節的多變性,抽象的東西要穩定的多。
我們以抽象的介面為基礎來架構我們的項目比以細節為基礎的架構要穩定的多。
總結
依賴倒置原則就是我們的抽象父類指向子類或者是抽象介面指向實現類的思想。
平常開發多留意,你一定會發現很多地方用到了這個原則思想。
但是這個設計也有一個缺點就是,我們在具體的實現類中實現了介面中沒有的方法,那麼此時這個方法是調用不了的,為什麼?後面的jvm系列我們會單獨來分析這一塊。
所以,比較理想的狀態就是使用介面或抽象類制定好規範,具體的實現類嚴格參考該介面或者抽象類。