目錄
1.1 依賴倒置原則(Dependency inversion principle)
一 名詞解釋
1.1 依賴倒置原則(Dependency inversion principle)
依賴倒置原則來源於軟件設計 6 大設計原則,它的定義如下:
上層模塊不應該依賴底層模塊,它們都應該依賴於抽象。
抽象不應該依賴於細節,細節應該依賴於抽象。
說明:高層模塊就是調用端,低層模塊就是具體實現類。抽象就是指接口或抽象類。細節就是實現類。
通俗來講: 依賴倒置原則的本質就是通過抽象(接口或抽象類)使個各類或模塊的實現彼此獨立,互不影響,實現模塊間的鬆耦合。
問題描述: 類A直接依賴類B,假如要將類A改爲依賴類C,則必須通過修改類A的代碼來達成。這種場景下,類A一般是高層模塊,負責複雜的業務邏輯;類B和類C是低層模塊,負責基本的原子操作;假如修改類A,會給程序帶來不必要的風險。
解決方案: 將類A修改爲依賴接口interface,類B和類C各自實現接口interface,類A通過接口interface間接與類B或者類C發生聯繫,則會大大降低修改類A的機率。
1.2 上層/底層模塊
類比公司,管理層就是上層,CEO 是整個事業羣的上層,那麼 CEO 職能之下就是底層。各部門經理以上部分是上層,那麼之下的組織都可以稱爲底層。由此,我們可以看到,在一個特定體系中,上層模塊與底層模塊可以按照決策能力高低爲準繩進行劃分。那麼,映射到我們軟件實際開發中,一般我們也會將軟件進行模塊劃分,比如業務層、邏輯層和數據層。
業務層中是軟件真正要進行的操作,也就是做什麼。 邏輯層是軟件現階段爲了業務層的需求提供的實現細節,也就是怎麼做。 數據層指業務層和邏輯層所需要的數據模型。如前面所總結,按照決策能力的高低進行模塊劃分。業務層自然就處於上層模塊,邏輯層和數據層自然就歸類爲底層。
1.3 依賴(Dependency)
簡單一點說,A類中持有B類的引用,A類即依賴於B類。下面例子中,Person持有Car的引用,Person依賴於Car。
public class Person {
private Car mCar;
public Person() {
mCar = new Car();
}
}
二 依賴倒置
2.1 依賴倒置前
在之前的開發中,我們會這樣寫代碼:
public class Person {
private Bike mBike;
private Car mCar;
private Plane mPlane;
public Person(){
mBike = new Bike();
}
public void goToBeijing(){
System.out.println("以某種交通方式去北京");
mBike.drive();
}
public static void main(String ... args){
Person person = new Person();
person.goToBeijing();
}
}
我們創建了一個 Person 類,Person可以選擇任意一種交通方式去北京,當然不管哪種交通方式,開汽車也好坐飛機也好,每種交通方式都包含一個 goToBeijing()的方法供Person調用。
這是基礎代碼,類比實際代碼中,肯定會經常性地更改交通工具,這時會頻繁地更改Person中的依賴對象,也就是需要再Person類中new不同的交通工具(car or plane or bike),代碼更改,多、煩、耦合高。
有沒有方法能解決上述問題呢?依賴倒置!
2.1 依賴倒置後
在利用依賴倒置前,我們需要明白依賴倒置的定義:
- 上層模塊不應該依賴底層模塊,它們都應該依賴於抽象。
- 抽象不應該依賴於細節,細節應該依賴於抽象。
首先是上層模塊和底層模塊的拆分,Person依賴交通出具出行,Person 屬於上層模塊,Bike、Car 和 Train 屬於底層模塊。之前的代碼中,Person依賴於交通工具才能出行。
我們分析一下:Person只想去北京,去北京是抽象。具體怎麼去北京,坐飛機還是開汽車,這是細節。上層模塊也就是Person他應該依賴於抽象而不是細節,細節只要實現好去北京具體乘坐哪個交通方式(也就是方法的實現)就好了。
public class Person {
private Bike mBike;
private Car mCar;
private Plane mplane;
private Driveable mDriveable;
public Person(){
mDriveable = new Plane();
}
public void goToBeijing(){
System.out.println("以某種交通方式去北京");
mDriveable.drive();
}
public static void main(String ... args){
Person person = new Person();
person.goToBeijing();
}
}
Person無論選擇哪種出行方式,去北京只要調用goToBeijing方法即可。該方法會自動調用出行交通工具的drive方法,因爲bike也好坐小汽車也好,坐飛機也好,這三種出行方式都是實現了Driveable接口。該接口就是一個抽象,它不在乎你去北京的具體實現,但無論你選擇哪種交通出行方式,都必須drive到北京。
可以看到,依賴倒置實質上是面向接口編程的體現。
三 控制反轉 (IoC)
上述例子中,我們仍然需要對Person類內部進行代碼修改,還是沒有達到目的。因爲Person仍然掌控着自己內部的mDriveable 具體實現類的實例化。 我們需要將 mDriveable 的實例化移到 Person 外面,也就是說,Person將自己掌控着的內部 mDriveable 的實例化的這個控制權反轉交出去了,這就是控制反轉,也就是 IoC (Inversion of Control) 。
public class Person {
private Driveable mDriveable;
public Person(Driveable driveable){
this.mDriveable = driveable;
}
public void goToBeijing(){
System.out.println("以某種交通方式去北京");
mDriveable.drive();
}
public static void main(String ... args){
Person2 person = new Person2(new Car());
person.goToBeijing();
}
}
就這樣無論Person選擇什麼交通方式去北京,都不需要再去更改Person 類的內部了。Person 把內部依賴的創建權力移交給了類中的 main() 方法,它只關心依賴提供的功能,而不關心依賴的創建,不再親自創建 Driveable 對象。而Person這個類在 IoC 中,指代了 IoC 容器這個概念。
這種思想其實就是 IoC,IoC 是一種新的設計模式,它對上層模塊與底層模塊進行了更進一步的解耦。控制反轉的意思是反轉了上層模塊對於底層模塊的依賴控制。
四 依賴注入(Dependency injection)
上面是控制反轉,那麼如何實現控制反轉呢?
答:依賴注入 ,DI , Dependency Injection。也經常被簡稱爲 DI,它是一種實現 IoC 的手段。
上面的例子中我們移交出了Person對於依賴實例化的控制權,那麼依賴怎麼辦?Person 無法實例化依賴了,它就需要在外部(IoC 容器)賦值給它,這個賦值的動作有個專門的術語叫做注入(injection)。
需要注意的是在 IoC 概念中,這個注入依賴的地方被稱爲 IoC 容器,但在依賴注入概念中,一般被稱爲注射器 (injector)。
表達通俗一點就是:我不想自己實例化依賴,你(injector)創建它們,然後在合適的時候注入給我
實現依賴注入有 3 種方式:
-
構造函數中注入
-
setter 方式注入
-
接口注入
/**
* 接口方式注入
* 接口的存在,表明了一種依賴配置的能力。
*/
public interface DepedencySetter {
void set(Driveable driveable);
}
public class Person implements DepedencySetter {
//接口方式注入
@Override
public void set(Driveable driveable) {
this.mDriveable = mDriveable;
}
private Driveable mDriveable;
//構造函數注入
public Person(Driveable driveable){
this.mDriveable = driveable;
}
//setter 方式注入
public void setDriveable(Driveable mDriveable) {
this.mDriveable = mDriveable;
}
public void goToBeijing(){
System.out.println("以某種交通方式去北京");
mDriveable.drive();
}
public static void main(String ... args){
Person2 person = new Person2(new Car());
person.goToBeijing();
}
}
參考文章:
輕鬆學,淺析依賴倒置(DIP)、控制反轉(IOC)和依賴注入(DI):https://blog.csdn.net/briblue/article/details/75093382