文章目錄
- 1. 簡介
- 2. 控制反轉 IoC
-
- 2.1 概念的了解
- 2.2 例子說明
- 3. 依賴倒轉原則 DIP
-
- 3.1 定義
- 3.2 例子
- 4. 依賴注入DI
-
- 4.1 依賴注入的類型
- 4.2 例子 略
參考:https://www.tutorialsteacher.com/ioc
作者本人在學習Ioc時,始終不了解IoC的本質什麼,很多中文資料在我看來比較難懂,于是搜尋英文資料,終于找到一篇比較淺顯易懂的文章。原文代碼是C#寫的,本人用Java重寫。
1. 簡介
你可能聽說過以下幾個名詞:
中文 | 英文 | 英文縮寫 |
---|---|---|
控制反轉 | Inversion of Control | IoC |
依賴倒轉原則 | Dependency Inversion Principle | DIP |
依賴注入 | Dependency Injection | DI |
IoC容器 | Ioc Container | 無 |
但是你可能不清楚他們之間的差別。
如上圖所示,IoC和DIP是處于上層的設計原則(Principle),也正因為是原則,是以沒有提供具體的實作細節,這些原則是我們設計類時需要遵循的原則。DI是模式(Pattern),Ioc容器是架構(Framework)。
讓我們先簡單過一下上面的幾個概念。
- 控制反轉 IoC
IoC是一種設計原則,它建議在面向對象設計中反轉(Inversion)各種控制(Control),以達到在類之間的松耦合(Loose Coupling)。在這裡,控制更多指的是類的附加職責,而不是它的主要職責,比如控制應用的流程或控制所依賴類的建立和綁定。
- 依賴倒轉原則 DIP
DIP同樣可以達到類之間的松耦合,強烈建議同時使用IoC和DIP來達到松耦合設計。
DIP建議高層子產品(high-level modules)不應該依賴低層子產品(low-levle modules),兩者都應該依賴抽象(abstaction, )。在java中,抽象指的是抽象類或者接口。
- 依賴注入 DI
DI是一種設計模式(Pattern),用來實作IoC,以達到反轉依賴類的建立的目的。
- IoC容器
IoC容器是一種架構,用來管理應用程式中的自動依賴注入,是以我們程式猿可以節省體力。著名的spring中就提供IoC容器ApplicationContext。
下圖所示,我們一步一步地學習從耦合類到松耦合類的設計過程。
強耦合類 -> 使用工廠模式實作IoC -> 通過建立抽象實作DIP -> 實作DI -> 使用IoC容器 - > 松耦合類。
2. 控制反轉 IoC
2.1 概念的了解
學習一個東西,首先了解概念。先解釋一下控制反轉的概念,分成控制和反轉兩個部分。
控制:更多指的是控制類的附加職責,而不是它的主要職責,比如控制應用的流程或控制所依賴類的建立和綁定。
反轉:指的将附加職責交給其他類來完成,而不是自己來完成。
比如,你開車去上班,那麼意味着你控制着車,反轉就是你不自己開車,而是坐的士去上班,車的控制權從你的手上轉移到了計程車司機,你就不用操心開車的事,不用操心車停哪裡,不用操心車的保養維修等等,你隻需要關注自己的工作即可。
個人的簡單了解:聚焦于自己主要的職責,把次要的職責外包出去,以此達到類之間松耦合的目的。
2.2 例子說明
public class A {
private B b;
public A() {
b = new B();
}
public void task() {
// do something here
b.someMethod();
// do something here
}
}
public class B {
public void someMethod() {
// do something
}
}
如上面的代碼,A調用B類的someMethod()方法,換句話說A依賴于B。A類直接控制B類的建立和生命周期。Ioc原則推薦反轉控制,就是說把B類的建立交給其他類。如下代碼:
public class A {
private B b;
public A() {
b = Factory.getObjectOfB();
}
public void task() {
// do something here
b.someMethod();
// do something here
}
}
public class B {
public void someMethod() {
// do something
}
}
public class Factory {
public static B getObjectOfB() {
return new B();
}
}
你可以看到B類的建立已經交給Factory了,A隻需要拿來用就行,專注于自己的主要職責task()方法。這就是控制反轉。
3. 依賴倒轉原則 DIP
3.1 定義
- 高層子產品不應該依賴底層子產品,兩者都應該依賴抽象;
- 抽象不應該依賴細節,細節應該依賴抽象。
3.2 例子
public class CustomerBusinessLogic {
public CustomerBusinessLogic() {
}
public String getCustomerName(int id) {
DataAccess dataAccess = DataAccessFactory.GetDataAccessObj();
return dataAccess.getCustomerName(id);
}
}
public class DataAccess {
public DataAccess() {
}
public String getCustomerName(int id) {
// get it from DB in real app
return "Dummy Customer Name";
}
}
public class DataAccessFactory {
public static DataAccess GetDataAccessObj() {
return new DataAccess();
}
}
以上的例子,我們用簡單工廠方法實作IoC原則,但是CustomerBusinessLogic直接用DataAccess,還是緊耦合。
正如DIP定義,高層子產品不應該依賴低層子產品,所謂高層子產品就是依賴其他子產品的子產品,例如這裡的CustomerBusinessLogic,那麼被依賴的DataAccess就是低層子產品;另外一個原則是抽象不依賴細節,細節依賴抽象。這裡的抽象是什麼? 在java中,就是抽象類和接口。
public class CustomerBusinessLogic {
private IDataAccess dataAccess;
public CustomerBusinessLogic() {
this.dataAccess = DataAccessFactory.GetDataAccessObj();
}
public String getCustomerName(int id) {
return dataAccess.getCustomerName(id);
}
}
public interface IDataAccess {
String getCustomerName(int id);
}
public class DataAccess implements IDataAccess {
public DataAccess() {
}
@Override
public String getCustomerName(int id) {
// get it from DB in real app
return "Dummy Customer Name";
}
}
public class DataAccessFactory {
public static IDataAccess GetDataAccessObj() {
return new DataAccess();
}
}
如上,高層子產品CustomerBusinessLogic和低層子產品DataAccess都依賴于抽象IDataAccess,細節DataAccess依賴于抽象IDataAccess。
4. 依賴注入DI
DI是一種設計模式,使用DI,我們可以将類的建立和綁定在依賴類的外部完成。簡單的說A依賴于B,B的建立和綁定在A類的操作都不在A類内完成,而是其他類中完成。
實作DI涉及到三種類:
- client class:用戶端類,依賴服務類的類;
- service class:服務類,給client提供服務的類;
- injector class:注入類,建立服務類,并且把服務類注入用戶端類的類。
4.1 依賴注入的類型
有三種方式實作依賴注入:
- Constructor Injection:構造函數注入;
- Property Injection:屬性注入,也即setter方法注入;
- Method Injection:方法注入,