[深入理解laravel前提] 一步一步從依賴注入和控制反轉實現一個超級簡單IOC容器 適合小白食用

syxuwen發表於2021-08-26

懵懂時,我一直不理解為什麼要有IOC容器這東西,composer不是已經可以實現自動載入了嗎?

一個開發框架有很多的類庫、例項等等需要管理和呼叫,IOC容器可以讓我們更好的管理我們的類和物件,當前主流的框架已經離不開它了

別走客官,這是學習武林祕籍第一步,想要習得上層武功,必經之路呀,否則你只能去隔壁練葵花寶典了!

學習它之前,我們需要弄清2個概念

  1. 依賴注入
  2. 控制反轉
    最後我們將實現一個簡單的自動依賴注入的IOC容器

依賴注入

我想 new 一個例項,但是這個例項中還需要 new 另外一個例項,也就是說兩個例項對應的類是包含(組合)關係。

別逼逼,上程式碼

class A {
    private $objB;
    public function __construct (B $obj) {
        $this->objB = $obj;
    }
}
class B {}

//想拿A就得new B,嘿嘿
$instance = new A(new B());

依賴注入表示的是兩個類的例項之間的共存關係

控制反轉

為什麼要有控制反轉呢,上面的依賴注入是我們手動完成的,當某個物件需要另外一個物件的時候,我們就直接手動給它 new 一個出來,有沒有更加高效的方式呢?IOC 控制反轉應運而生,因為不需要你手動new就能自動產生你需要物件,怎麼樣,很神奇吧!

說到控制反轉,我們不得不提的程式語言的反射機制,試想一下 new 一個類的時候,我只要知道類名就能例項化,是不是意味著只要你給我名字,我就控制了你這個類,那麼我們稱之為反射。

  1. 告訴我你需要的類的名字叫A,我通過一個叫ReflectionClass 來獲得他的反射物件$reflector
    $reflector = new ReflectionClass('App\A');
  2. isInstantiable() 檢查類是否可例項化,newInstance()獲得一個例項
    if ($reflector->isInstantiable()) {
    return $refector->newInstance(); 
    }
    怎麼樣,能拿到想要的例項吧,試想一下,如果我們把這種好用的特性結合到依賴注入會怎麼樣呢,是不是意味著我們就不用手動 new 例項了,直接拿到類名,自動 new 唄,還要啥自行車。

一個類例項化肯定會呼叫構造方法 __construct() 我們抓住這一點

$constructor = $reflector->getConstructor();
$dependencies = $constructor->getParameters();//獲得構造方法的引數

你依賴的類這下全部給我在$dependencies中了吧,那麼我一個個new 不就來了麼

$dependenciesInstances = [];
foreach($dependencies as $dependency) {
    $reflector = new ReflectionClass($dependency);
    $dependenciesInstances[] = $refector->newInstance(); 
}

至此,我們是不是可以通過構造方法自動new依賴的例項了,效率大大的提高。

實現一個很簡單的自動依賴注入工廠類

下面我們來寫一個簡單的自動依賴注入的工廠來解決我們開頭說重複 new 的問題

class IocFactory {
    /**
     * make依賴例項
     * @param $class
     * @return object
     * @throws ReflectionException
     */
    public static function make($class) {
        $reflector = new ReflectionClass($class);
        $constructor = $reflector->getConstructor();

        if (is_null($constructor)) {
            if ($reflector->isInstantiable()) {
                return $reflector->newInstance();
            }
        }

        $dependenciesArgs = [];
        $dependencies = $constructor->getParameters();//獲得構造方法的引數
        foreach($dependencies as $dependency ) {
            //例項化依賴
            $dependenciesArgs[] = self::make($dependency->getName());
        }
        return $reflector->newInstanceArgs($dependenciesArgs);
    }
}

下面我們宣告三個類,A類包含B類包含C類 也就是 A=>B=>C

class A {
    private $b;
    public function __construct(B $b)
    {
        $this->b = $b;
    }
}
class B {
    private $c;
    public function __construct(C $c)
    {
        $this->c = $c;
    }
}
class C {

}

現在可以例項化A,就可以自動幫我們把B和C也例項化

$obj = IocFactory::make(A::class);
var_dump($obj);

結果

object(A)#6 (1) {
  ["b":"A":private]=>
  object(B)#7 (1) {
    ["c":"B":private]=>
    object(C)#8 (0) {
    }
  }
}
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章