淺談依賴注入與控制反轉

前言:設計模式實際上是一個很空洞的東西,設計模式有幾十種,有些人以爲工廠模式也單例模式已經足夠解決大部分問題。而有些人以爲任何設計模式都會讓開發變得更「複雜」,更「低效」。因此千萬不要太過追求他的實際意義和做用,不然你已經墜入雲霧。可是無論怎麼樣,實際工做中仍是要對它們有所瞭解,下面從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類方法

經過反射咱們能夠獲得該類的所有信息,包括方法、方法名、屬性甚至註釋等等。經過反射咱們能夠方便的控制程序中使用的類,對他們進行擴展、修正、以及監聽。一般反射在插件開發和框架開發中大量應用。在框架開發中也會把反射與依賴注入、控制反轉搭配使用,讓程序有強大的可控性和擴展性。

博客連接:淺談依賴注入與控制反轉

相關文章
相關標籤/搜索