解析PHP依賴注入和控制反轉

來源:互聯網
上載者:User
這篇文章主要介紹了PHP依賴注入(DI)和控制反轉(IoC)的相關資料,具有一定的參考價值,感興趣的小夥伴們可以參考一下

首先依賴注入和控制反轉說的是同一個東西,是一種設計模式,這種設計模式用來減少程式間的耦合,鄙人學習了一下,看TP官網還沒有相關的文章,就寫下這篇拙作介紹一下這種設計模式,希望能為TP社區貢獻一些力量。

首先先別追究這個設計模式的定義,否則你一定會被說的雲裡霧裡,筆者就是深受其害,百度了N多文章,都是從理論角度來描述,充斥著大量的生澀詞彙,要麼就是java代碼描述的,也生澀。

不管怎麼樣,總算弄清楚一些了,下面就以php的角度來描述一下依賴注入這個概念。

先假設我們這裡有一個類,類裡面需要用到資料庫連接,按照最最原始的辦法,我們可能是這樣寫這個類的:


class example {    private $_db;  function __construct(){    include "./Lib/Db.php";    $this->_db = new Db("localhost","root","123456","test");  }  function getList(){    $this->_db->query("......");//這裡具體sql語句就省略不寫了  } }

過程:

在建構函式裡先將資料庫類檔案include進來;
然後又通過new Db並傳入資料庫連接資訊執行個體化db類;
之後getList方法就可以通過$this->_db來調用資料庫類,實現資料庫操作。

看上去我們實現了想要的功能,但是這是一個噩夢的開始,以後example1,example2,example3....越來越多的類需要用到db組件,如果都這麼寫的話,萬一有一天資料庫密碼改了或者db類發生變化了,豈不是要回頭修改所有類檔案?
ok,為瞭解決這個問題,原廠模式出現了,我們建立了一個Factory方法,並通過Factory::getDb()方法來獲得db組件的執行個體:


class Factory {  public static function getDb(){    include "./Lib/Db.php";    return new Db("localhost","root","123456","test");  } }

sample類變成:


class example {    private $_db;  function __construct(){    $this->_db = Factory::getDb();  }  function getList(){    $this->_db->query("......");//這裡具體sql語句就省略不寫了  } }

這樣就完美了嗎?再次想想一下以後example1,example2,example3....所有的類,你都需要在建構函式裡通過Factory::getDb();獲的一個Db執行個體,實際上你由原來的直接與Db類的耦合變為了和Factory工廠類的耦合,工廠類只是幫你把資料庫連接資訊給封裝起來了,雖然當資料庫資訊發生變化時只要修改Factory::getDb()方法就可以了,但是突然有一天Factory 方法需要改名,或者getDb方法需要改名,你又怎麼辦?當然這種需求其實還是很操蛋的,但有時候確實存在這種情況,一種解決方式是:

我們不從example類內部執行個體化Db組件,我們依靠從外部的注入,什麼意思呢?看下面的例子:


class example {  private $_db;  function getList(){    $this->_db->query("......");//這裡具體sql語句就省略不寫了  }  //從外部注入db串連  function setDb($connection){    $this->_db = $connection;  } } //調用$example = new example();$example->setDb(Factory::getDb());//注入db串連$example->getList();

這樣一來,example類完全與外部類解除耦合了,你可以看到Db類裡面已經沒有Factory 方法或Db類的身影了。我們通過從外部調用example類的setDb方法,將串連執行個體直接注入進去。這樣example完全不用關心db串連怎麼產生的了。
這就叫依賴注入,實現不是在代碼內部建立依賴關係,而是讓其作為一個參數傳遞,這使得我們的程式更容易維護,降低程式碼的耦合度,實現一種松耦合。

這還沒完,我們再假設example類裡面除了db還要用到其他外部類,我們通過:


$example->setDb(Factory::getDb());//注入db串連$example->setFile(Factory::getFile());//注入檔案處理類$example->setImage(Factory::getImage());//注入Image處理類 ...

我們沒完沒了的寫這麼多set?累不累?
ok,為了不用每次寫這麼多行代碼,我們又去弄了一個Factory 方法:


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時變為:


$example=Factory::getExample();$example->getList();

似乎完美了,但是怎麼感覺又回到了上面第一次用Factory 方法時的情境?這確實不是一個好的解決方案,所以又提出了一個概念:容器,又叫做IoC容器、DI容器。

我們本來是通過setXXX方法注入各種類,代碼很長,方法很多,雖然可以通過一個Factory 方法封裝,但是還不是那麼爽,好吧,我們不用setXXX方法了,這樣也就不用Factory 方法二次封裝了,那麼我們還怎麼實現依賴注入呢?
這裡我們引入一個約定:在example類的建構函式裡傳入一個名為Di $di的參數,如下:


class example {  private $_di;  function __construct(Di &$di){    $this->_di = $di;  }  //通過di容器擷取db執行個體  function getList(){    $this->_di->get('db')->query("......");//這裡具體sql語句就省略不寫了  } }$di = new Di();$di->set("db",function(){  return new Db("localhost","root","root","test");  });$example = new example($di);$example->getList();

Di就是IoC容器,所謂容器就是存放我們可能會用到的各種類的執行個體,我們通過$di->set()設定一個名為db的執行個體,因為是通過回呼函數的方式傳入的,所以set的時候並不會立即執行個體化db類,而是當$di->get('db')的時候才會執行個體化,同樣,在設計di類的時候還可以融入單例模式。

這樣我們只要在全域範圍內申明一個Di類,將所有需要注入的類放到容器裡,然後將容器作為建構函式的參數傳入到example,即可在example類裡面從容器中擷取執行個體。當然也不一定是建構函式,你也可以用一個 setDi(Di $di)的方法來傳入Di容器,總之約定是你制定的,你自己清楚就行。

這樣一來依賴注入以及關鍵的容器概念已經介紹完畢,剩下的就是在實際中使用並理解它吧!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.