前言:設計模式實際上是一個很空洞的東西,設計模式有幾十種,有些人以爲工廠模式也單例模式已經足夠解決大部分問題。而有些人以爲任何設計模式都會讓開發變得更「複雜」,更「低效」。因此千萬不要太過追求他的實際意義和做用,不然你已經墜入雲霧。可是無論怎麼樣,實際工做中仍是要對它們有所瞭解,下面從php的角度來說一下依賴注入、控制反轉、反射等概念。若有錯誤之處,還望路過大神多加指點php
首先設定場景,假如一個類須要數據庫鏈接,最簡單的作法多是:html
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 工廠類:sql
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) , 不在內部處理依賴關係,而是把依賴做爲參數傳遞進去,以下降程序的耦合性 。框架
而後咱們的項目繼續進行,用到了文件處理類,圖像處理類,咱們可能會這樣寫this
$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)code
可是這樣又有一些比較有強迫症的同窗發現了,每一個類都要注入一遍容器是否是有些麻煩。沒錯,其實注入容器這個動做能夠交給另外的程序處理,那就是反射。
<?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類方法
經過反射咱們能夠獲得該類的所有信息,包括方法、方法名、屬性甚至註釋等等。經過反射咱們能夠方便的控制程序中使用的類,對他們進行擴展、修正、以及監聽。一般反射在插件開發和框架開發中大量應用。在框架開發中也會把反射與依賴注入、控制反轉搭配使用,讓程序有強大的可控性和擴展性。
博客連接:淺談依賴注入與控制反轉