Laravel框架下容器Container 的依賴注入和反射應用

  依賴注入,簡單說是把類裏頭依賴的對象,置於類外頭,即客戶端調用處。至關於把類與類解耦。php

  一個簡單的例子:laravel

  

class A {

    public function __construct() {
        // 這種實例化寫法, A類的內部,依賴了B類 須要解耦
        $this->b = new B();
    }

    public function say(){
        $this->b->talk();
    }
}


class B{

    public function __construct() {

    }

    public function talk(){
        echo __METHOD__;
    }
}


$a = new A;
$a->say();

  A類依賴B類,耦合較重。數組

 

  換一種寫法:閉包

  

class A {

    public function __construct(B $b) {// B的實例化,放在客戶端,看成構造參數傳遞過來
        $this->b = $b;
    }

    public function say(){
        $this->b->talk();
    }
}


class B{
    public function __construct() {
    }

    public function talk(){
        echo __METHOD__;
    }
}


$b = new B;// 在客戶端實例化B
$a = new A($b);// 構造器中傳遞
$a->say();

這個代碼,就至關於實例化B的步驟放在了A類的外部,從而實現解耦。這就是依賴注入的一種實現方式。框架

 

 

Laravel框架中,經過這種依賴注入的方式,再配合反射功能,實現功能。函數

好比在Laravel的容器類,Container.php(vendor/laravel/framework/src/illuminate/Container/Container.php)中this

利用反射,實例化注入的類。spa

 

來一段代碼:code

/**
 *
 * 依賴注入的思想,把外部依賴的類放在構造函數的參數裏
 *
 * Laravel框架根據反射 搞定依賴
 */

class Test{

    public function __construct(Para $p1, Parb $p2) {
        echo $p1->getInfo()," ",$p2->getInfo()," ";
    }

    function say(){
        echo __CLASS__;
        return;
    }
}

/**
 * Class Para
 * 參數A
 */
class Para {

    function getInfo() {
        echo __CLASS__;
    }
}

/**
 * Class Parb
 * 參數B
 */
class Parb {

    function getInfo(){
        echo __CLASS__;
    }
}


$obj = new Test(new Para(), new Parb());

// ===================================================

$reflector = new ReflectionClass($obj);// 對象 反射其類信息
$constructor = $reflector->getConstructor();

$dependencies = $constructor->getParameters();// 獲取構造器下的參數信息
$parArr = array();
foreach ($dependencies as $depend){
    // $depend->getClass()->name 獲取類名稱 構造器參數是類
    $parArr[] = new ($depend->getClass()->name)();
}

$refNew = $reflector->newInstanceArgs($parArr);// Test 對象

$refNew->say();

 

代碼中,獲取$obj反射後的類信息,再取其構造器中的依賴類,實例化這些依賴類,再傳入Test類中。  對象

這就是Laravel容器中實現的依賴注入和反射應用。

 

下邊的代碼有助於理解Laravel框架下的容器Container概念。參考自文章:https://www.insp.top/article/learn-laravel-container

<?php


interface SuperModuleInterface{
    public function activate(array $target);
}

class Superman
{
    protected $module;

    /**
     * Superman constructor.
     * @param SuperModuleInterface $module
     * 經過構造器 注入依賴
     */
    public function __construct(SuperModuleInterface $module)
    {
        $this->module = $module;
    }

    public function show(array $target){

        $this->module->activate($target);
    }
}

class PowerA implements SuperModuleInterface
{
    public function activate(array $target)
    {
        echo '<pre>'. __METHOD__;
        print_r(func_get_args());
    }
}

class PowerB implements SuperModuleInterface
{
    public function activate(array $target)
    {
        echo '<pre>'. __METHOD__;
        print_r(func_get_args());
    }
}



class Container
{
    protected $binds;

    protected $instances;

    /**
     * @param $abstract
     * @param $concrete
     * 把代詞 綁定到容器裏,爲後續make
     */
    public function bind($abstract, $concrete)
    {
        if ($concrete instanceof Closure) {
            $this->binds[$abstract] = $concrete;
        } else {
            $this->instances[$abstract] = $concrete;
        }
    }

    /**
     * @param $abstract
     * @param array $parameters
     * @return mixed
     * 建立對象
     */
    public function make($abstract, $parameters = [])
    {
        if (isset($this->instances[$abstract])) {
            return $this->instances[$abstract];
        }
        // 把容器對象$this,放到參數數組第一個元素。爲call_user_func_array使用
        array_unshift($parameters, $this);
        
        // 這裏$this->binds[$abstract] 綁定的閉包函數, 執行函數參數是$parameters
        return call_user_func_array($this->binds[$abstract], $parameters);
    }
}


// 建立一個容器(後面稱做超級工廠)
$container = new Container;


// 向該 超級工廠添加超能力模組的生產腳本
$container->bind('powerA', function($container) {
    return new PowerA;
});

// 同上
$container->bind('powerB', function($container) {
    return new PowerB;
});


// 向該 超級工廠添加超人的生產腳本
$container->bind('superman', function($container, $moduleName) {
    return new Superman($container->make($moduleName));
});



echo "<pre>";

// 開始啓動生產
$superman_1 = $container->make('superman', ['powerA']);
$superman_1->show(['a' => 1]);

$superman_2 = $container->make('superman', ['powerB']);
$superman_2->show(['b' => 1]);
相關文章
相關標籤/搜索