您好,登錄后才能下訂單哦!
在軟件工程領域,依賴注入(Dependency Injection)是用于實現控制反轉(Inversion of Control)的最常見的方式之一。本文主要介紹依賴注入原理和常見的實現方式,重點在于介紹這種年輕的設計模式的適用場景及優勢。
首先我們來一個實例,上代碼
<?php class A { public function test() { echo 'this is A!<br>'; $b = new B(); $b->test(); } } class B { public function test() { echo 'this is B!<br>'; $c = new C(); $c->test(); } } class C { public function test() { echo 'this is C!<br>'; } } $obj = new A(); $obj->test();
結果是:
this is A! this is B! this is C!
從代碼分析,A類依賴B類,B類依賴C類。這是我們最原始的實現思路.這種實現思路很明顯會有問題
假如我們現在B類修改下,代碼如下:
class B { public $name; public function __construct($name) { $this->name = $name; } public function test() { echo 'this is B'.$this->name.'!<br>'; $c = new C(); $c->test(); } }
此時再看我們原來A類test方法直接調用明顯會有問題,于是此時我們需要將原來代碼:
class A { public function test() { echo 'this is A!<br>'; $b = new B(); $b->test(); } }
修改成:
class A { public function test() { echo 'this is A!<br>'; $b = new B('(class B)');//構造的時候多加了一個參數 $b->test(); } }
如果此時C類構造方法也變動了呢,B的方法里面也同樣受影響,很明顯可用性非常低
為了解耦,此時我們用到第二種思路:構造器注入,直接上代碼:
<?php class A { public $obj; public function __construct(B $b) { $this->obj = $b; } public function test() { echo 'this is A!<br>'; $this->obj->test(); } } class B { public $obj; public function __construct(C $c) { $this->obj = $c; } public function test() { echo 'this is B!<br>'; $this->obj->test(); } } class C { public function test() { echo 'this is C!<br>'; } } $c = new C(); $b = new B($c); $obj = new A($b); $obj->test();
這種方法可以解決第一種方法,如果依賴的類構造方法(比如B類)有所改動,A類不需要改動任何代碼。但是久而久之,這種方法毛病也出來了,我們首先必須要先實例化C類,再實例化B類,最后再實例化A類。
了解到第二種方案需要優化,于是出現了基本成型的第三種方案,此時我們用到了container(容器)和instance(實例),直接上代碼:
class Container { public static $_definations = []; public function get($class) { //當前類所依賴的類 $depends = []; $tc = new ReflectionClass($class); //得到構造方法 $constructor = $tc->getConstructor(); //得到構造方法的參數 if($constructor !== NULL) { foreach($constructor->getParameters() as $parameter) { if($parameter->isDefaultValueAvailable()) { $depends[] = $parameter->getDefaultValue(); } else { $pc = $parameter->getClass(); $instance = Instance::getInstance($pc == NULL ? NULL : $pc->getName()); $depends[] = $instance; } } } foreach($depends as $k => $v) { if($v instanceof Instance) { if($v->id !== NULL) { $depends[$k] = $this->get($v->id); } } } $tm_instance = $tc->newInstanceArgs($depends); return $tm_instance; } } class Instance{ /** * @var 類唯一標示 */ public $id; /** * 構造函數 * @param string $id 類唯一ID * @return void */ public function __construct($id) { $this->id = $id; } /** * 獲取類的實例 * @param string $id 類唯一ID * @return Object Instance */ public static function getInstance($id) { return new self($id); } } class Base{ } class A extends Base{ private $instanceB; public function __construct(B $instanceB) { $this->instanceB = $instanceB; } public function test() { echo 'this is A!<br/>'; $this->instanceB->test(); } } class B extends Base{ private $instanceC; public function __construct(C $instanceC) { $this->instanceC = $instanceC; } public function test() { echo 'this is B!<br/>'; return $this->instanceC->test(); } } class C extends Base{ public function test() { echo 'this is C!'; } } $container = new Container(); $obj_a = $container->get('A'); $obj_a->test();
此方法有參考yii2中yii2\di\container實現,只是將它簡化了,更容易看懂。重點看看container的get方法
現在我們增加set方法,自定義函數,直接上代碼精簡版
class Container { /** *@var array 存儲各個類的定義 以類的名稱為鍵 */ public static $_definations = []; public function get($class, $params = [], $props = []) { if(!isset(self::$_definations[$class])) return $this->build($class, $params, $props); //如果已經被定義過的 $_defination = self::$_definations[$class]; if(is_callable($_defination, true)) { return call_user_func($_defination, $this, $params, $props); } else if(is_object($_defination)) { return $_defination; } else { throw new Exception($class . '聲明錯誤'); } } public function set($class, $_defination) { self::$_definations[$class] = $_defination; } public function build($class, $params, $props) { //當前類所依賴的類 $depends = []; $tc = new ReflectionClass($class); //得到構造方法 $constructor = $tc->getConstructor(); //得到構造方法的參數 if($constructor !== NULL) { foreach($constructor->getParameters() as $parameter) { if($parameter->isDefaultValueAvailable()) { $depends[] = $parameter->getDefaultValue(); } else { $pc = $parameter->getClass(); $instance = Instance::getInstance($pc == NULL ? NULL : $pc->getName()); $depends[] = $instance; } } } foreach($depends as $k => $v) { if($v instanceof Instance) { if($v->id !== NULL) { $depends[$k] = $this->get($v->id); } } } $tm_instance = $tc->newInstanceArgs($depends); return $tm_instance; } }
增加了一個set方法,并將get方法稍微改版了一下,現在我們看看怎么調用set方法:
$container = new Container(); $container->set('foo', function($container, $params, $config){ print_r($params); print_r($config); }); $container->get('foo', ['p1' => 'pv1'], ['c1' => 'cv1']);
輸出結果為:
Array ( [p1] => pv1 ) Array ( [c1] => cv1 )
再看看另外一種情況調用:
class Test { public function mytest() { echo 'this is a test'; } } $container->set('testObj', new Test()); $test = $container->get('testObj'); $test->mytest();
輸出結果為:
this is a test
上面的代碼只是作者粗略的寫了下簡單版本,很多地方不是太完善,這版本是為了理解yii2的container打下基礎,稍后會出yii2的依賴注入源碼分析
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。