依賴注入,簡單說是把類裏頭依賴的對象,置於類外頭,即客戶端調用處。至關於把類與類解耦。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]);