標籤:現實生活 參數 cti 查詢 管理 決定 通過 stat 最好
前段時間,有朋友問我yii2的依賴注入是怎麼個玩法,好吧,
經常看到卻一直不甚理解的概念,這裡我再對自己認識的依賴注入深刻的表達下我的理解,依賴注入(DI)以及控制器反轉(Ioc)。 依賴注入就是組件通過構造器,方法或者屬性欄位來擷取相應的依賴對象。
舉個現實生活中的例子來理解, 比如我要一把菜刀 如何獲得
1.可以自己造一把,對應new一個。
2.可以找生產菜刀的工廠去買一把,對應原廠模式。
3.可以打電話 讓店家送貨上門,對應依賴注入
依賴注入(DI)的概念雖然聽起來很深奧,但是如果你用過一些新興的php架構的話,對於DI一定不陌生,因為它們多多少少都用到了依賴注入來處理類與類之間的依賴關係。
php中傳遞依賴關係的三種方案
其實要理解DI,首先要明白在php中如何傳遞依賴關係。
第一種方案,也是最不可取的方案,就是在A類中直接用new關鍵詞來建立一個B類,如下代碼所示:
<?phpclass A{ public function __construct() { $b = new B(); }}
為什麼這種方案不可取呢?因為這樣的話,A與B就耦合在了一起,也就是說A類無法脫離B類工作。
第二種方案就是在A類的方法中傳入需要的B類,如下代碼所示:
<?phpclass A{ public function __construct(B $b) { }}
這種方法比第一種方案有了改進,A類不必與B類捆綁在一起,只要傳入的類滿足A類的需求,也可以是C類,也可以是D類等等。
但是這種方案的弊端在於如果A類依賴的類較多,參數列表會很長,容易發生混亂。
第三種方案是使用set方法傳入,如下代碼所示:
<?phpclass A{ public function setB(B $b) { $this->b = $b; }}
這種方案同樣存在和第二種方案一樣的弊端,當依賴的類增多時,我們需要些很多很多的set方法。
這時我們在想如果有一個專門的類(或者說一個容器)可以幫我們管理這些依賴關係就好了。
一個簡單的依賴注入的例子
如下代碼來自twittee:
<?phpclass Container { private $s=array(); function __set($k, $c) { $this->s[$k]=$c; } function __get($k) { return $this->s[$k]($this); }}
有了container類之後我們可以怎樣管理A與B之間的依賴關係呢,用代碼說話吧:
<?phpclass A{ private $container; public function __construct(Container $container) { $this->container = $container; } public function doSomeThing() { //do something which needs class B $b = $this->container->getB(); //to do }}
再將B類注入到容器類中:
$c = new Container();$c->setB(new B());
還可以傳入一個匿名函數,這樣B類就不會在傳入時就立即執行個體化,而是在真正調用時才完成執行個體化的工作:
$c = new Container();$c->setB(function (){ return new B();});
這裡舉的只是一個很簡單的例子,在實際中,容器類要考慮的有很多,比如消極式載入等等。
再比如我是一個演員,我不可能要求某個導演,我要演某某劇的男一號,相反,導演可以決定讓誰來演。而我們的object就是這個演員。
注入的幾個途徑:
1.construct注入
<?phpclass Book { private $db_conn; public function __construct($db_conn) { $this->db_conn = $db_conn; }}
但是如果依賴過多,那麼在構造方法裡必然傳入多個參數,三個以上就會使代碼變的難以閱讀。
2.set注入
<?php $book = new Book(); $book->setdb($db); $book->setprice($price); $book->set_author($author);?>
代碼很清晰,但是當我們需要注入第四個依賴時,意味著又要增加一行。
比較好的解決辦法是 建立一個class作為所有依賴關係的container,在這個class中可以存放、建立、擷取、尋找需要的依賴關係
<?phpclass Ioc { protected $db_conn; public static function make_book() { $new_book = new Book(); $new_book->set_db(self::$db_conn); //... //... //其他的依賴注入 return $new_book; }}
此時,如果擷取一個book執行個體,只需要執行$newone = Ioc::makebook();
以上是container的一個具體執行個體,最好還是不要把具體的某個依賴注入寫成方法,採用registry註冊,get擷取比較好。
<?phpclass Ioc {/*** @var 註冊的依賴數組*/ protected static $registry = array(); /** * 添加一個resolve到registry數組中 * @param string $name 依賴標識 * @param object $resolve 一個匿名函數用來建立執行個體 * @return void */ public static function register($name, Closure $resolve) { static::$registry[$name] = $resolve; } /** * 返回一個執行個體 * @param string $name 依賴的標識 * @return mixed */ public static function resolve($name) { if ( static::registered($name) ) { $name = static::$registry[$name]; return $name(); } throw new Exception(‘Nothing registered with that name, fool.‘); } /** * 查詢某個依賴執行個體是否存在 * @param string $name id * @return bool */ public static function registered($name) { return array_key_exists($name, static::$registry); }}
現在就可以通過如下方式來註冊和注入一個依賴
<?php$book = Ioc::registry(‘book‘, function(){$book = new Book;$book->setdb(‘...‘);$book->setprice(‘...‘);return $book;}); //注入依賴$book = Ioc::resolve(‘book‘);?>
再說php依賴注入