浅谈依赖注入与控制反转
前言:設計模式其實是一個很空洞的東西,設計模式有幾十種,有些人覺得工廠模式也單例模式已經足夠解決大部分問題。而有些人覺得任何設計模式都會讓開發變得更“復雜”,更“低效”。所以千萬不要太過追求他的實際意義和作用,否則你已經墜入云霧。但是不管怎么樣,實際工作中還是要對它們有所了解,下面從php的角度來講一下依賴注入、控制反轉、反射等概念。如有錯誤之處,還望路過大神多加指點
首先設定場景,假如一個類需要數據庫連接,最簡單的做法可能是:
class example {private $_db; function __construct(){ include "./Lib/Db.php"; $this->_db = new Db("localhost","root","123456","test"); } function getList(){ $this->_db->query("......"); } }但事實上稍微有點經驗的同學都不會這樣寫,因為一旦越來越多的類用到db,而db一旦發生變化,那么豈不是要每個文件都修改一遍?這就是程序設計中的耦合問題。所有的類過度依賴?"./Lib/Db.php"?這個文件。OK,為了解決這個問題,工廠模式出現了,我們新建一個 Factory 工廠類:
class Factory {public static function getDb(){ include "./Lib/Db.php"; return new Db("localhost","root","123456","test"); } } class example { private $_db; function __construct(){ $this->_db = Factory::getDb(); } function getList(){ $this->_db->query("......"); } }如果我們用到db模塊那么直接?Factory::getDb()?從工廠中取出來就是了,看似解決了問題。但事實是這樣嗎?不,這樣只不過是把程序與 db 模塊的耦合轉移到了 Factory ,一旦后期業務發生變動,Factory 發生變動,依舊要對每個文件改動。那如何解決呢?
我們可以不從example類內部獲取db組件,我們從外部把db組件注入進example類
class example {private $_db; function getList(){ $this->_db->query("......");//執行查詢 } //從外部注入db連接 function setDb($connection){ $this->_db = $connection; } } $example = new example(); $example->setDb(Factory::getDb());//注入db連接 $example->getList();這樣一來example就不用關心db組件怎么來的了,只用暴露一個注入方法即可。這就是DI/依賴注入(Dependency Injection),不在內部處理依賴關系,而是把依賴作為參數傳遞進去,以降低程序的耦合性。
然后我們的項目繼續進行,用到了文件處理類,圖像處理類,我們可能會這樣寫
$example->setDb(Factory::getDb());//注入db連接 $example->setFile(Factory::getFile());//注入文件處理類 $example->setImage(Factory::getImage());//注入Image處理類但是這樣似乎也不行啊,每次都要寫這么多代碼,于是我們又寫了一個工廠方法
class Factory {public static function getExample(){ $example = new example(); $example->setDb(Factory::getDb());//注入db連接 $example->setFile(Factory::getFile());//注入文件處理類 $example->setImage(Factory::getImage());//注入Image處理類 return $expample; } }example也不直接new 了。我們用 Factory::getExample()中獲取。但是,這是不是又有點熟悉的感覺?和上面第一次用工廠類的時候一樣依賴于工廠。于是又有了容器的概念。
class example {private $_di; function __construct(Di &$di){ $this->_di = $di; } //通過di容器獲取db實例 function getList(){ $this->_di->get('db')->query("......"); } } $di = new Di(); $di->set("db",function(){ return new Db("localhost","root","root","test"); }); $example = new example($di); $example->getList();Di就是一個存放各種可擴展的組件的容器,需要注入的時候調用$di->set()方法注入組件即可。程序中即可通過$di->get() 獲取組件。這樣被調用的組件(db)并不是由調用者(example)創建,而是由Di容器創建,調用者失去控制權,而容器得到控制權,發生了控制權轉移,所以叫做控制反轉(Inversion of Control)
但是這樣又有一些比較有強迫癥的同學發現了,每個類都要注入一遍容器是不是有些麻煩。沒錯,其實注入容器這個動作可以交給另外的程序處理,那就是反射。
<?php /** * example */ class example { //通過di容器獲取db實例 function getList(){ $this->_di->get('db')->query("......"); } } //di容器 class Di{ public $_container; public function get($cls){ return $this->_container[$cls]; } public function set($cls,$_instance){ $this->_container[$cls]=$_instance; } } //db組件 class db{ private static $_instance;//保存單例 //單例方法 public static function getInstance(){ if(!(self::$_instance instanceof self)){ self::$_instance = new self; } return self::$_instance; } //查詢方法 public function query($sql){ echo $sql; } } $di = new Di();//實例化容器 $di->set('db',db::getInstance()); //注入db實例 $reflector = new ReflectionClass('example'); //反射example,通過反射可以獲得該類的所有信息 $reflector->getDefaultProperties(); //example屬性 $reflector->getDocComment(); //注釋 $instance =$reflector->newInstanceArgs(); //相當于實例化反射的example類 $instance->_di=$di; //注入di容器 $reflector->getmethod('getList')->invoke($instance);//調用example類方法通過反射我們可以得到該類的全部信息,包括方法、方法名、屬性甚至注釋等等。通過反射我們可以方便的控制程序中使用的類,對他們進行擴展、修正、以及監聽。通常反射在插件開發和框架開發中大量應用。在框架開發中也會把反射與依賴注入、控制反轉搭配使用,讓程序有強大的可控性和擴展性。
?
eg:反射主要是用在“單一出口”的情況下使用,比如mvc框架,基本上都是由框架核心類解析url,得到指向的文件名類名方法名,然后加載對應的控制器文件,然后用反射注入容器就可以了,并不需要每次都寫。
轉載于:https://www.cnblogs.com/yhdsir/p/5751173.html
總結
以上是生活随笔為你收集整理的浅谈依赖注入与控制反转的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 自定义admin
- 下一篇: 通过severlet获取请求头信息