深入理解IoC(控制反轉)、DI(依賴注入)

estu發表於2019-02-16

引述

最近看設計模式以及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_LoginUser_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的框架是不能修改的,應該就是通過這種方式實現的呼叫方的自定義。

相關文章