依賴注入,又稱(chēng)為控制反轉(zhuǎn)(Inversion of Control,英文縮寫(xiě)為IoC)是一個(gè)重要的面向?qū)ο缶幊痰姆▌t來(lái)削減計(jì)算機(jī)程序的耦合問(wèn)題,也是輕量級(jí)的Spring框架的核心。 控制反轉(zhuǎn)一般分為兩種類(lèi)型,依賴注入(Dependency Injection,簡(jiǎn)稱(chēng)DI)和依賴查找(Dependency Lookup)。依賴注入應(yīng)用比較廣泛。IoC 可以認(rèn)為是一種全新的設(shè)計(jì)模式
以上摘自百度百科,概念我們都知道是抽象的,難以理解的,要多在實(shí)際應(yīng)用去慢慢體會(huì)。舉個(gè)例子:
假如讓我們組建一支足球隊(duì)代表國(guó)家去參加世界杯,全球有這么多不同的國(guó)家,大家都想要參加比賽,那么首先,國(guó)家都有一個(gè)足球隊(duì)是不是,所以 國(guó)家依賴足球隊(duì)(團(tuán)隊(duì)),但是團(tuán)隊(duì)是由球員組成的是不是,所以團(tuán)隊(duì)依賴球員,有了這樣的關(guān)系,我們就可以建立一些遵循這些關(guān)系的類(lèi),很簡(jiǎn)單,核心的三個(gè)類(lèi)不能少,國(guó)家(Country),團(tuán)隊(duì)(Team),球員(Player),看代碼:
class Country {}
class Player {}
class Team {}
這些類(lèi)目前都是“空”的。讓我們想一想, 首先一個(gè) Country 得有個(gè)基本屬性 name 吧,比如就交 PRC,它依賴一個(gè) Team,
以對(duì)象作為參數(shù)傳遞
class Country
{
protected $team;
protected $name;
public function __construct(Team $team, $name = 'PRC')
{
$this->team = $team;
$this->name = $name;
}
}
這里,我們傳遞了兩個(gè)參數(shù)給構(gòu)造函數(shù),第一個(gè)參數(shù)就是使用了 Type Hinting,你可以稱(chēng)為類(lèi)型約束或類(lèi)型提示,來(lái)達(dá)到依賴注入。第二個(gè)參數(shù)就是簡(jiǎn)單的國(guó)家名稱(chēng),默認(rèn) PRC,到這里我們國(guó)家有了,因?yàn)橛忻至税。?依賴一個(gè)團(tuán)隊(duì) Team,但是 Team 還是空的,別急,讓我們給 Team 加點(diǎn)球員,沒(méi)有球員打 J8 球,還得是大牌,來(lái)定義一個(gè) join 方法:
class Team
{
protected $players = [];
public function __construct($players = [])
{
$this->players = $players;
}
public function join(Player $player)
{
$this->players[] = $player;
}
public function getPlayers()
{
return $this->players;
}
}
首先,我們定義了一個(gè) players 數(shù)組屬性用來(lái)存儲(chǔ)我們的球員,當(dāng)我們組建一個(gè)團(tuán)隊(duì)的時(shí)候,我們將自動(dòng)生成一個(gè) players 球員數(shù)組來(lái)控制加入團(tuán)隊(duì)的球員。在 join 方法中再一次以 Type Hinting 的方式傳遞一個(gè) Player 實(shí)例,只不過(guò)這一次實(shí)在普通方法里傳遞參數(shù)。最后用一個(gè)簡(jiǎn)單的獲取方法 getPlayers 來(lái)獲取我們加入到團(tuán)隊(duì)的球員,不然,這個(gè) Team 類(lèi),是不是就只進(jìn)不出了。接下來(lái),我們要給我們的球員取個(gè)名字,要不然都不知道有哪些大牌加入我們的 Team 呢:
class Player
{
protected $name;
public function __construct($name)
{
$this->name = $name;
}
}
好像到目前為止,依賴關(guān)系比較清晰了呢,但我們還有個(gè)小問(wèn)題,怎樣讓球員加入指定的國(guó)家呢?比如讓梅西為我大天朝效力。所以,很顯然國(guó)家必須有招募球員的能力:
發(fā)送消息
class Country
{
protected $team;
protected $name;
public function __construct(Team $team, $name = 'PRC')
{
$this->team = $team;
$this->name = $name;
}
public function recruit(Player $player)
{
$this->team->join($player);
}
}
現(xiàn)在國(guó)家就用了招募球員到他們自己的團(tuán)隊(duì)里去了,這里我們可以將這個(gè)過(guò)程稱(chēng)為消息傳遞,message passing,方法的名稱(chēng)和輸入的參數(shù)被概念化為消息,看成類(lèi)與類(lèi),類(lèi)與對(duì)象,對(duì)象與對(duì)象之間的通信?;仡^再梳理一遍依賴關(guān)系,正如前面所說(shuō),國(guó)家依賴團(tuán)隊(duì),所以我們?cè)?Country 的構(gòu)造函數(shù)里傳遞了 Team 實(shí)例,那么實(shí)現(xiàn)這個(gè)過(guò)程,我們就應(yīng)該首先創(chuàng)建一個(gè) Team 實(shí)例,然后創(chuàng)建一個(gè) Country 實(shí)例,并傳遞 Team 實(shí)例給 Country:
$team = new Team;
$china = new Country($team);
OK,接下來(lái)招募 Players 到 Team 中:
$player1 = new Player('梅西');
$china->recruit($player1);
然后,讓我們確認(rèn)下是不是梅西就到了國(guó)家隊(duì)了:
$team->getPlayers();
Array
(
[0] => Player Object
(
[name:protected] => 梅西
)
)
至此梅西已經(jīng)來(lái)到國(guó)家隊(duì)為我們征戰(zhàn)世界杯了,誒,不對(duì),足球不是一個(gè)人的游戲誒,梅西再牛逼沒(méi)有隊(duì)友怎么玩,繼續(xù)招募球員:
$player2 = new Player('C羅');
$player3 = new Player('內(nèi)馬爾');
$player4 = new Player('科比');
$player5 = new Player('詹姆斯');
…………
…………
$usa->recruit($player2);
$usa->recruit($player3);
$usa->recruit($player4);
$usa->recruit($player5);
$team->getplayers();
至此,新一屆的國(guó)家隊(duì)已經(jīng)成立,這陣容世界杯妥妥的。
總結(jié):
很簡(jiǎn)單的依賴注入的小示例,首先我們并沒(méi)有為了完成這個(gè)業(yè)務(wù)邏輯而創(chuàng)建一個(gè)大而全的類(lèi),試圖將所有的功能放在一個(gè)位置,而是分成很多業(yè)務(wù)邏輯比較單一和清晰的小類(lèi),即使這些類(lèi)是分開(kāi)的, 他們也能夠通過(guò) message passing 或彼此調(diào)用方法來(lái)通信。是不是感受到松耦合,容易維護(hù)的地方了,比如足球隊(duì)不要了,來(lái)個(gè)軍隊(duì)要開(kāi)戰(zhàn)了,招募點(diǎn)敢死隊(duì)是不是很方便?