PHP依賴注入(DI)和控制反轉(zhuǎn)(IoC)詳解
首先依賴注入和控制反轉(zhuǎn)說(shuō)的是同一個(gè)東西,是一種設(shè)計(jì)模式,這種設(shè)計(jì)模式用來(lái)減少程序間的耦合,鄙人學(xué)習(xí)了一下,看TP官網(wǎng)還沒(méi)有相關(guān)的文章,就寫下這篇拙作介紹一下這種設(shè)計(jì)模式,希望能為TP社區(qū)貢獻(xiàn)一些力量。
首先先別追究這個(gè)設(shè)計(jì)模式的定義,否則你一定會(huì)被說(shuō)的云里霧里,筆者就是深受其害,百度了N多文章,都是從理論角度來(lái)描述,充斥著大量的生澀詞匯,要么就是java代碼描述的,也生澀。
不管怎么樣,總算弄清楚一些了,下面就以php的角度來(lái)描述一下依賴注入這個(gè)概念。
先假設(shè)我們這里有一個(gè)類,類里面需要用到數(shù)據(jù)庫(kù)連接,按照最最原始的辦法,我們可能是這樣寫這個(gè)類的:
class example {
private $_db;
function __construct(){
include "./Lib/Db.php";
$this->_db = new Db("localhost","root","123456","test");
}
function getList(){
$this->_db->query("......");//這里具體sql語(yǔ)句就省略不寫了
}
}
過(guò)程:
在構(gòu)造函數(shù)里先將數(shù)據(jù)庫(kù)類文件include進(jìn)來(lái);
然后又通過(guò)new Db并傳入數(shù)據(jù)庫(kù)連接信息實(shí)例化db類;
之后getList方法就可以通過(guò)$this->_db來(lái)調(diào)用數(shù)據(jù)庫(kù)類,實(shí)現(xiàn)數(shù)據(jù)庫(kù)操作。
看上去我們實(shí)現(xiàn)了想要的功能,但是這是一個(gè)噩夢(mèng)的開始,以后example1,example2,example3....越來(lái)越多的類需要用到db組件,如果都這么寫的話,萬(wàn)一有一天數(shù)據(jù)庫(kù)密碼改了或者db類發(fā)生變化了,豈不是要回頭修改所有類文件?
ok,為了解決這個(gè)問(wèn)題,工廠模式出現(xiàn)了,我們創(chuàng)建了一個(gè)Factory方法,并通過(guò)Factory::getDb()方法來(lái)獲得db組件的實(shí)例:
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語(yǔ)句就省略不寫了
}
}
這樣就完美了嗎?再次想想一下以后example1,example2,example3....所有的類,你都需要在構(gòu)造函數(shù)里通過(guò)Factory::getDb();獲的一個(gè)Db實(shí)例,實(shí)際上由原來(lái)的直接與Db類的耦合變?yōu)榱撕虵actory工廠類的耦合,工廠類只是幫你把數(shù)據(jù)庫(kù)連接信息給包裝起來(lái)了,雖然當(dāng)數(shù)據(jù)庫(kù)信息發(fā)生變化時(shí)只要修改Factory::getDb()方法就可以了,但是突然有一天工廠方法需要改名,或者getDb方法需要改名,你又怎么辦?當(dāng)然這種需求、、但有時(shí)候確實(shí)存在這種情況,一種解決方式是:
我們不從example類內(nèi)部實(shí)例化Db組件,我們依靠從外部的注入,什么意思呢?看下面的例子:
class example {
private $_db;
function getList(){
$this->_db->query("......");//這里具體sql語(yǔ)句就省略不寫了
}
//從外部注入db連接
function setDb($connection){
$this->_db = $connection;
}
}
//調(diào)用
$example = new example();
$example->setDb(Factory::getDb());//注入db連接
$example->getList();
這樣一來(lái),example類完全與外部類解除耦合了,你可以看到Db類里面已經(jīng)沒(méi)有工廠方法或Db類的身影了。我們通過(guò)從外部調(diào)用example類的setDb方法,將連接實(shí)例直接注入進(jìn)去。這樣example完全不用關(guān)心db連接怎么生成的了。
這就叫依賴注入,實(shí)現(xiàn)不是在代碼內(nèi)部創(chuàng)建依賴關(guān)系,而是讓其作為一個(gè)參數(shù)傳遞,這使得我們的程序更容易維護(hù),降低程序代碼的耦合度,實(shí)現(xiàn)一種松耦合。
這還沒(méi)完,我們?cè)偌僭O(shè)example類里面除了db還要用到其他外部類,我們通過(guò):
$example->setDb(Factory::getDb());//注入db連接 $example->setFile(Factory::getFile());//注入文件處理類 $example->setImage(Factory::getImage());//注入Image處理類 ...
我們沒(méi)完沒(méi)了的寫這么多set?累不累?
ok,為了不用每次寫這么多行代碼,我們又去弄了一個(gè)工廠方法:
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;
}
}
實(shí)例化example時(shí)變?yōu)椋?/p>
$example=Factory::getExample(); $example->getList();
似乎完美了,但是怎么感覺(jué)又回到了上面第一次用工廠方法時(shí)的場(chǎng)景?這確實(shí)不是一個(gè)好的解決方案,所以又提出了一個(gè)概念:容器,又叫做IoC容器、DI容器。
我們本來(lái)是通過(guò)setXXX方法注入各種類,代碼很長(zhǎng),方法很多,雖然可以通過(guò)一個(gè)工廠方法包裝,但是還不是那么爽,好吧,我們不用setXXX方法了,這樣也就不用工廠方法二次包裝了,那么我們還怎么實(shí)現(xiàn)依賴注入呢?
這里我們引入一個(gè)約定:在example類的構(gòu)造函數(shù)里傳入一個(gè)名為Di $di的參數(shù),如下:
class example {
private $_di;
function __construct(Di &$di){
$this->_di = $di;
}
//通過(guò)di容器獲取db實(shí)例
function getList(){
$this->_di->get('db')->query("......");//這里具體sql語(yǔ)句就省略不寫了
}
}
$di = new Di();
$di->set("db",function(){
return new Db("localhost","root","root","test");
});
$example = new example($di);
$example->getList();
Di就是IoC容器,所謂容器就是存放我們可能會(huì)用到的各種類的實(shí)例,我們通過(guò)$di->set()設(shè)置一個(gè)名為db的實(shí)例,因?yàn)槭峭ㄟ^(guò)回調(diào)函數(shù)的方式傳入的,所以set的時(shí)候并不會(huì)立即實(shí)例化db類,而是當(dāng)$di->get('db')的時(shí)候才會(huì)實(shí)例化,同樣,在設(shè)計(jì)di類的時(shí)候還可以融入單例模式。
這樣我們只要在全局范圍內(nèi)申明一個(gè)Di類,將所有需要注入的類放到容器里,然后將容器作為構(gòu)造函數(shù)的參數(shù)傳入到example,即可在example類里面從容器中獲取實(shí)例。當(dāng)然也不一定是構(gòu)造函數(shù),你也可以用一個(gè) setDi(Di $di)的方法來(lái)傳入Di容器,總之約定是你制定的,你自己清楚就行。
這樣一來(lái)依賴注入以及關(guān)鍵的容器概念已經(jīng)介紹完畢,剩下的就是在實(shí)際中使用并理解它吧!
相關(guān)文章
常見(jiàn)的四種POST 提交數(shù)據(jù)方式(小總結(jié))
這篇文章主要介紹了常見(jiàn)的四種POST 提交數(shù)據(jù)方式(小總結(jié))的相關(guān)資料,需要的朋友可以參考下2015-10-10
php中magic_quotes_gpc對(duì)unserialize的影響分析
這篇文章主要介紹了php中magic_quotes_gpc對(duì)unserialize的影響,以實(shí)例的形式分析了magic_quotes_gpc安全過(guò)濾對(duì)unserialize造成的影響以及對(duì)此的解決方法,非常具有實(shí)用價(jià)值,需要的朋友可以參考下2014-12-12
PHP utf-8編碼問(wèn)題,utf8編碼,數(shù)據(jù)庫(kù)亂碼,頁(yè)面顯示輸出亂碼
老聲長(zhǎng)談,著是困惑很多人的問(wèn)題,如果處理不好,都是亂碼,說(shuō)這些話并不是我對(duì)編碼很精通,只是在這方面是得留神,自己總結(jié)了一點(diǎn)小經(jīng)驗(yàn)2013-04-04
PHP使用PDO實(shí)現(xiàn)mysql防注入功能詳解
這篇文章主要介紹了PHP使用PDO實(shí)現(xiàn)mysql防注入功能,結(jié)合實(shí)例形式詳細(xì)分析了PHP使用pdo操作mysql防注入原理、實(shí)現(xiàn)方法及相關(guān)注意事項(xiàng),需要的朋友可以參考下2019-12-12
PHP實(shí)現(xiàn)的數(shù)組和XML文件相互轉(zhuǎn)換功能示例
這篇文章主要介紹了PHP實(shí)現(xiàn)的數(shù)組和XML文件相互轉(zhuǎn)換功能,結(jié)合實(shí)例形式分析了php針對(duì)xml格式數(shù)據(jù)與數(shù)組相互轉(zhuǎn)換操作技巧,需要的朋友可以參考下2018-03-03
關(guān)于ob_get_contents(),ob_end_clean(),ob_start(),的具體用法詳解
本篇文章是對(duì)ob_get_contents(),ob_end_clean(),ob_start(),的具體用法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-06-06
PHP圖片處理之使用imagecopyresampled函數(shù)裁剪圖片例子
這篇文章主要介紹了PHP圖片處理之使用imagecopyresampled函數(shù)裁剪圖片例子,本文例子相對(duì)簡(jiǎn)單,是一篇入門級(jí)的學(xué)習(xí)總結(jié),需要的朋友可以參考下2014-11-11
PHP遞歸實(shí)現(xiàn)文件夾的復(fù)制、刪除、查看大小操作示例
這篇文章主要介紹了PHP遞歸實(shí)現(xiàn)文件夾的復(fù)制、刪除、查看大小操作,結(jié)合實(shí)例形式分析了php遞歸算法結(jié)合php常用文件夾操作函數(shù)實(shí)現(xiàn)文件夾的復(fù)制、刪除、查看等相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-08-08

