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

巴扎黑
發布: 2023-03-14 19:16:01
原創
2680 人瀏覽過

這篇文章主要介紹了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()方法就可以了,但是突然有一天工廠方法需要改名,或是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類別裡面已經沒有工廠方法或Db類的身影了。我們透過從外部呼叫example類別的setDb方法,將連線實例直接注入進去。這樣example完全不用關心db連線怎麼產生的了。
這就叫做依賴注入,實現不是在程式碼內部創建依賴關係,而是讓其作為一個參數傳遞,這使得我們的程式更容易維護,降低程式碼的耦合度,實現一種松耦合。

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


$example->setDb(Factory::getDb());//注入db连接
$example->setFile(Factory::getFile());//注入文件处理类
$example->setImage(Factory::getImage());//注入Image处理类
 ...
登入後複製

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


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();
登入後複製

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

我們本來是透過setXXX方法注入各種類,程式碼很長,方法很多,雖然可以透過一個工廠方法包裝,但是還不是那麼爽,好吧,我們不用setXXX方法了,這樣也就不用工廠方法二次包裝了,那我們該怎麼實現依賴注入呢?
這裡我們引入一個約定:在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容器,總之約定是你制定的,你自己清楚就行。

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

以上是解析PHP依賴注入與控制反轉的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!