依賴注入,簡單說是把類里頭依賴的對象,置於類外頭,即客戶端調用處。相當於把類與類解耦。
一個簡單的例子:
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)中
利用反射,實例化注入的類。
來一段代碼:
/** * * 依賴注入的思想,把外部依賴的類放在構造函數的參數里 * * 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]);