Laravel Inversion of Control (控制反轉) 概念簡介

Ίκαρος發表於2017-09-29

本文內容部分摘自 Wikipedia - Inversion of Control .

IoC (控制反轉),是物件導向程式設計中的一種設計原則,可以用來減低計算機程式碼之間的耦合度。

實現「控制反轉」,有兩種方式:

  • Dependency Injection (DI) - 依賴注入
  • Dependency Lookup - 依賴查詢

兩者的區別在於,前者是被動的接收物件,在類例項建立過程中即建立了依賴的物件,透過型別或名稱來判斷將不同的物件注入到不同的屬性中,而後者是主動索取相應型別的物件,獲得依賴物件的時間也可以在程式碼中自由控制。

哪些方面的「控制」被「反轉」了?

依賴物件的「獲得」被反轉了。

技能描述

Class A 中用到了 Class B 的物件 b,一般情況下,需要在 A 的程式碼中顯式的 new 一個 B 的物件。採用依賴注入技術之後,A 的程式碼只需要定義一個私有的B物件,不需要直接 new 來獲得這個物件,而是透過相關的 容器控制程式 來將 B 物件在外部 new 出來並注入到 A 類裡的引用中。而具體獲取的方法、物件被獲取時的狀態由 容器 來指定。

假設我有一個 iPhone,我的 iPhone 依賴充電器才能充電。

class iPhone
{
    // 電量
    private $power;

    // 充電
    public function charge()
    {
    }
}

我還有個蘋果原裝充電器:

class AppleCharger
{
    public function charge()
    {
        return 100;
    }
}

在以前,iPhone 內部「控制」著只能用哪一款充電器:

class iPhone
{
    // 電量
    private $power;

    // 充電,只能用原裝的充電器
    public function charge()
    {
        $charger = new AppleCharger;
        $this->power = $charger->charge();
    }
}
// 充電
$iphone = new iPhone;
$iphone->charge();

使用依賴注入之後,我來決定給 iPhone 用哪一款充電器:

class iPhone
{
    private $power;
    private $charger;

    // 依賴注入充電器,╮(╯▽╰)╭哎算了只要是充電器就行
    public function __construct(Charger $charger)
    {
        $this->charger = $charger;
    }

    // 充電
    public function charge()
    {
        $this->power = $this->charger->charge();
    }
}
interface Charger
{
    public function charge();
}
// Laravel 容器
use Illuminate\Container\Container;
$container = Container::getInstance();

// 給它一個原裝充電器:
$container->bind(Charger::class, AppleCharger::class);

// 或者給它其它充電器
$container->bind(Charger::class, OtherCharger::class);

// 充電
$iphone = $container->make(iPhone::class);
$iphone->charger();

可見,使用依賴注入之後,控制權「反轉」了,由外部來決定給它什麼充電器 (依賴物件)。
Laravel 管這個 容器控制程式Service Container (服務容器),它來控制著各種依賴的獲取方法。

更多關於 Laravel 依賴注入、服務容器的知識可以參考以前的文章。
Laravel Dependency Injection (依賴注入) 概念詳解
Laravel Container (容器) 概念詳解 (上)
Laravel Container (容器) 深入理解 (下)

依賴注入實現方式:

  • 基於建構函式。實現特定引數的建構函式,在新建物件時傳入所依賴型別的物件。
  • 基於介面。實現特定介面以供外部容器注入所依賴型別的物件。
  • 基於 set 方法。實現特定屬性的 public set 方法,來讓外部容器呼叫傳入所依賴型別的物件。
  • 基於註解。基於 Java 的註解功能,在私有變數前加 "@Autowired" 等註解,不需要顯式的定義以上三種程式碼,便可以讓外部容器傳入對應的物件。該方案相當於定義了 public 的 set 方法,但是因為沒有真正的 set 方法,從而不會為了實現依賴注入導致暴露了不該暴露的介面(因為 set 方法只想讓容器訪問來注入而並不希望其他依賴此類的物件訪問)。

依賴查詢:

依賴查詢更加主動,在需要的時候透過呼叫框架提供的方法來獲取物件,獲取時需要提供相關的配置檔案路徑、key等資訊來確定獲取物件的狀態。

本作品採用《CC 協議》,轉載必須註明作者和本文連結
原創。 所有 Laravel 文章均已收錄至 Github laravel-tips 專案。

相關文章