引述
最近看設計模式以及laravel程式碼,對於控制反轉以及依賴注入這些概念非常困惑,於是找了一些資料,以下是對於控制反轉的一下理解。
概念
IoC
控制反轉(Inversion of Control,縮寫為IoC),是物件導向程式設計中的一種設計原則,可以用來減低計算機程式碼之間的耦合度。它把傳統上由程式程式碼直接操控的物件的呼叫權交給容器,通過容器來實現物件元件的裝配和管理。所謂的“控制反轉”概念就是對元件物件控制權的轉移,從程式程式碼本身轉移到了外部容器。其中最常見的方式叫做依賴注入(Dependency Injection,簡稱DI),還有一種方式叫“依賴查詢”(Dependency Lookup)。通過控制反轉,物件在被建立的時候,由一個調控系統內所有物件的外界實體,將其所依賴的物件的引用傳遞給它。也可以說,依賴被注入到物件中。
正如概念中描述的,那麼控制反轉具體指哪些 控制 被 反轉 了呢,其實我理解的控制反轉是指程式所控制的物件的控制權交給了容器,而這個容器就是實現各個模組解耦合的關鍵。IoC只是一種設計思想,主要實現有:
- 依賴查詢(Dependency Lookup):容器提供回撥介面和上下文環境給元件。
- 依賴注入(Dependency Injection)
我們著重說一下依賴注入。
DI
在軟體工程中,依賴注入是種實現控制反轉用於解決依賴性設計模式。一個依賴關係指的是可被利用的一種物件(即服務提供端) 。依賴注入是將所依賴的傳遞給將使用的從屬物件(即客戶端)。該服務是將會變成客戶端的狀態的一部分。 傳遞服務給客戶端,而非允許客戶端來建立或尋找服務,是本設計模式的基本要求。
以上是wikipedia上對於依賴注入的理解,下面將用實際的例子詳細講解。
舉例
加入我們設計一個通用的使用者登入基類,可以支援多個平臺的使用者登入
那麼最糟糕的寫法是,
/**
* A類使用者登入
**/
class User_A_Login
{
public function checkALogin(){
};
}
/**
* B類使用者登入
**/
class User_B_Login
{
public function checkBLogin(){
};
}
/**
* 登入基類
**/
class User_Login
{
public function checkLogin($userType)
{
if($userType = `A`){
$this->objAUser = new User_A_Login();
$this->objAUser->checkALogin();
}elseif($userType = `B`){
$this->objBUser = new User_B_Login();
$this->objBUser->checkBLogin();
}
}
}
上面的 User_Login 這個類就直接依賴於類 User_A_Login 和 User_B_Login ,如果我們使用下面這種方式寫
interface User_Login_Interface
{
public function checkLogin();
}
/**
* A類使用者登入
**/
class User_A_Login implements User_Login_Interface
{
public function checkLogin(){
};
}
/**
* B類使用者登入
**/
class User_B_Login implements User_Login_Interface
{
public function checkLogin(){
};
}
/**
* 登入基類
**/
class User_Login
{
public $userLogin;
public function setUser(User_Login_Interface $user){
$this->userLogin = $user;
}
public function checkLogin()
{
$this->userLogin->checkLogin();
}
}
如果按照這種方式進行構建程式碼,我們將依賴項User_B_Login或者User_A_Login通過函式 setUser( ) 注入到類中,呼叫方式:
$userLogin = new User_Login();
$userLogin->setUser(new User_A_Login);
$userLogin->checkLogin();
呼叫方可以控制使用那個登入型別,這就完成了對於不同登入系統的依賴注入。如果你引入了一個新的使用者c,只需寫一個c的登入類:
/**
* C類使用者登入
**/
class User_C_Login implements User_Login_Interface
{
public function checkLogin(){
};
}
就可以通過:
$userLogin = new User_Login();
$userLogin->setUser(new User_C_Login);
$userLogin->checkLogin();
進行登入驗證了。在實際程式碼中由於基類非常複雜需要對登入做一系列處理,所以每次新增登入使用者不應該去修改基類。對於一下框架例如laravel的框架是不能修改的,應該就是通過這種方式實現的呼叫方的自定義。