控制反轉( IoC)和依賴註入(DI) tags: 容器 依賴註入 IOC DI 控制反轉 引言:如果你看過一些框架的源碼或者手冊,像是laravel或者tp5之類的,應該會提到容器,依賴註入,控制反轉等辭彙。或者是某些面試官會問到這類問題。希望這篇文章能讓你有所收穫。 1.1、IoC(控制反轉 I ...
控制反轉( IoC)和依賴註入(DI)
tags: 容器 依賴註入 IOC DI 控制反轉
引言:如果你看過一些框架的源碼或者手冊,像是laravel或者tp5之類的,應該會提到容器,依賴註入,控制反轉等辭彙。或者是某些面試官會問到這類問題。希望這篇文章能讓你有所收穫。
1.1、IoC(控制反轉 Inversion of Control)
簡述:控制反轉並不是一種技術,而是一種設計思想。通過控制反轉容器(以後稱容器),改變了原本某些對象運行時依賴其他對象資源時需要自己進行獲取(比如通過new ClassName),所造成的對象之間的強耦合(耦合的概念如果不理解,可以先去瞭解一下。)。
所謂IoC,對於程式來說,就是構造了一個容器,比如在tp5中,這個容器叫Container,此容器來負責控制對象和對象間的關係。在一個對象中,如果要使用另外的對象,就必須得到它(自己new一個),這樣的話一個對象A和另一個對象B之間就有了很強的聯繫(也就是強耦合)。這種耦合體現在如果被依賴的資源類初始化的時候(也可能是其他時候)傳入的參數變化了,你要修改兩個類的代碼,舉例說明:
你本來用的是php本身的session機制,現在想改成也支持redis,併在初始化的時候增加一個參數來控制。這個時候你不止需要去修改類Session類的代碼,還要去修改類A的代碼。部分代碼如下
//原代碼
class Session{
function __construct(){
//balabala
}
}
class A{
protected $driver;
function __construct(){
$this->driver = new Session();
}
}
//需要增加一種redis方式後修改為
class Session{
protected $driver;
function __construct(string $driver=''){
if($driver === ''){
//balabala
}else if($driver === 'redis'){//增加了一種模式
//balabala
}else{
//balabala
}
}
}
class A{
protected $driver;
function __construct(){
$this->driver = new Session('redis');//我想使用redis了
}
}
可以看到,不止我們修改了B的代碼,還要去修改A的代碼,這樣如果依賴關係多起來的話,每次修改A依賴的對象,可能要處理A中很多條代碼。會造成對象之間的強耦合。所以我們可以把A需要的東西,在A之前就創建出來,並通過構造函數參數傳遞給A。代碼如下
class A{
protected $driver;
function __construct(A $a){
//我想使用redis了
}
}
$driver = new Session('redis');//我想使用redis了
$a = new A($driver)
這種方法下更改A依賴的所有對象都通過構造方法或者其他方法的形式給A,這些對象本身機制更改的時候就無需修改A的代碼了。但是每次自己使用A的時候都去看看A需要些什麼在前面都new一遍,感覺上很low啊,不如我們搞一個管家(容器),如果發現你需要什麼,管家就給你什麼。這樣所依賴的類的創建都由容器來控制,也就是說控制對象創建的不再是引用它的對象,而是容器。對於某個具體的對象而言,以前是它控制其他對象,需要什麼自己處理,現在是所有對象都被容器控制,所以控制反轉是一種控制權的轉移。
1.2、DI(依賴註入 Dependency Injection Container)
IoC的一個重點是在系統運行中,動態的向某個對象提供它所需要的其他對象。這一點是通過DI(Dependency Injection,依賴註入,或者叫依賴註入容器)來實現的。比如還是上面的例子,對象A需要操作Session,以前我們總是要在A中自己編寫代碼來獲得一個Session對象,有了 容器這個管家我們就只需要告訴容器,A中需要一個Session對象,至於這個Session對象怎麼構造,何時構造,A不需要知道。在系統運行時,容器會在適當的時候製造一個Session,通過構造方法註射到A當中,這樣就完成了對各個對象之間關係的控制,而且這種關係是松耦合的。
A需要依賴 Session才能正常運行,而這個Session是由容器註入到A中的,依賴註入的名字就這麼來的。那麼DI是如何實現的呢? php有一個高級特性是反射(reflection),原理這裡大概說一下,
反射可以在php運行中,提取出關於類、方法、屬性、參數,註釋等的詳細信息,並可以動態的調用方法和類等。這種動態獲取的信息以及動態調用對象的方法的功能稱為反射API。
反射允許程式在運行的時候動態的生成對象、執行對象的方法、改變對象的屬性,容器就是通過反射來實現註入的。
總結:
控制反轉是說創建對象的控制權進行轉移,由原來的資源需求方,轉移到了容器,依賴註入是說本來是資源需求方依賴資源,現在資源需求方依賴於容器對資源的註入,可以看出來依賴註入和控制反轉說的其實是一個事情。
順便提一句,目前很多框架中都離不開反射功能。容器是一個典型的例子,容器在laravel和tp5中都是核心功能之一。