【Laravel—核心源碼解析】依賴注入、控制反轉(zhuǎn)、服務(wù)容器嘶卧、服務(wù)提供者

前言

想要看懂源碼首先得明白源碼中的主要思想,不然很難理解源碼中的實(shí)現(xiàn)過程凉袱,本章就著重講解Laravel框架中的服務(wù)容器芥吟、依賴注入、控制反轉(zhuǎn)专甩、服務(wù)提供者

正文

首先我們舉個(gè)例子
假設(shè)我們要開始一場(chǎng)旅行钟鸵,以代碼的方式來實(shí)現(xiàn)這場(chǎng)旅行,那既然是旅行肯定要選擇交通工具配深,我們用代碼這樣實(shí)現(xiàn):

//火車類
class Train
{
    //買票
    function buyTickets()
    {
    }
    //出發(fā)
    function go()
    {
    }
}
//旅行類
class Travel
{
    //出發(fā)
    function go()
    {
        //選擇火車
        $train = new Train();
        $train->buyTickets();
        $train->go();
    }
}

我們通常都是以這種方式實(shí)現(xiàn)代碼的結(jié)構(gòu)携添,清晰嫁盲、方便實(shí)現(xiàn)篓叶,但是如果我們又要旅行一次但這次我們不坐火車想坐飛機(jī)
那這時(shí)我們就需要修改旅行類的go方法(調(diào)用者),將實(shí)例化火車這個(gè)步驟修改為實(shí)例化飛機(jī)然后go~

//飛機(jī)類
class Aircraft
{
    //買票
    function buyTickets()
    {
    }

    //出發(fā)
    function go()
    {
    }
}
//旅行類
class Travel
{
    //出發(fā)
    function go()
    {
        //選擇飛機(jī)
        $train = new Aircraft();
        $train->buyTickets();
        $train->go();
    }
}

這種情況我們的調(diào)用者(旅游類)就依賴于被調(diào)用者(火車羞秤,飛機(jī)類)變成了強(qiáng)耦合缸托,如果我們要旅行好幾次,或者說我們不止是旅行瘾蛋,我們出差時(shí)也要用到這些交通工具相關(guān)的類俐镐,那我們?cè)侔鸦疖囶惛鼡Q為飛機(jī)類的時(shí)候是不是需要修改很多調(diào)用者呢?

工廠模式

這時(shí)我們?yōu)榱吮苊膺@種情況發(fā)生哺哼,引入了工廠類

//創(chuàng)建工廠類
class TrafficFactory
{
    public function getInstance()
    {
        //第一次旅行使用火車
//        $tool = new Train();
        //第二次旅行使用飛機(jī)
        $tool = new Aircraft();
        return $tool;
    }
}
//旅行類
class Travel
{
    //出發(fā)
    function go()
    {
        //獲取工廠類
        $factory = new TrafficFactory();
        //獲取這次的交通工具
        $tool = $factory->getInstance();
        //買票
        $tool->buyTickets();
        //出發(fā)
        $tool->go();
    }
}

這下我們不管旅行幾次換成什么交通工具我們都只需要去修改工廠類就可以了佩抹,而且我們的調(diào)用者也不再依賴于被調(diào)用者

但實(shí)際上需求總是那么的可怕,這次要新增的交通工具是汽車取董,自駕不需要買票(buyTickets)棍苹,但是要自己去加油,我們需要先創(chuàng)建汽車類

class Car
{
    function gasUp()
    {
    }
    function go()
    {

    }
}

我們一樣可以將汽車類加到工廠類中茵汰,但是我們?cè)谑褂脮r(shí)并不知道工廠給我們的是需要買票的飛機(jī)和火車還是需要加油的汽車

接口

這時(shí)就引入的接口類

interface Traffic
{
    //準(zhǔn)備枢里,不管有什么前置使用條件(加油,買票)都需要再次方法進(jìn)行實(shí)現(xiàn)
    public function ready();
    //出發(fā)
    public function go();
}

所有有關(guān)交通工具的類都需要遵守規(guī)則實(shí)現(xiàn)接口里的方法,所以進(jìn)行以下改造(這里為了不貼太多代碼我們只改造汽車類)

class Car implements Traffic
{
    function ready()
    {
        $this->gasUp();
    }
    function gasUp()
    {
    }
    function go()
    {
    }
}

這樣我們就將交通工具的使用方法給統(tǒng)一了
現(xiàn)在我們的調(diào)用者不直接依賴于被調(diào)用者栏豺,但是我們卻依賴于工廠類了彬碱,有什么辦法這個(gè)調(diào)用者與工廠進(jìn)行解耦呢?

依賴注入(DI)&控制反轉(zhuǎn)(IOC)

調(diào)用者不需要去關(guān)心用的是什么交通工具奥洼,也不需要知道要做什么準(zhǔn)備我們只管go巷疼,以依賴注入&控制反轉(zhuǎn)的思想進(jìn)行如下改造

class Travel
{
    //出發(fā)
    function go(Traffic $tool)
    {
        //出發(fā)
        $tool->go();
    }
}
//獲取交通工具工廠類
$factory = new TrafficFactory();
//獲取這次的交通工具
$tool = $factory->getInstance();
$travel = new Travel();
//準(zhǔn)備
$tool->ready();
$travel->go($tool);

這下調(diào)用者只管go,外面做什么他都不用管溉卓,就算是更換了其它交通工具甚至是更換了工廠也不關(guān)調(diào)用者的事了皮迟。
起先我們獲取交通工具是在調(diào)用者中做的,現(xiàn)在放到外部去了桑寨,這時(shí)控制權(quán)就不在調(diào)用者內(nèi)部這種變化稱為控制反轉(zhuǎn)伏尼,我們以傳參的形式將調(diào)用者所需的服務(wù)(交通工具)注入給調(diào)用者使用,這種行為稱為依賴注入

接下來隨著業(yè)務(wù)增多尉尾,需求不斷變化(上面的例子全是交通工具相關(guān)爆阶,旅行還會(huì)涉及用餐,住宿等等)沙咏,因?yàn)楣S類通常職責(zé)專一(也是面向?qū)ο蟮闹饕枷胫唬┪覀冃枰墓S也會(huì)越來越多工廠類變得越來越多

再者上面提到的交通工具辨图,用餐,住宿并不只適用與旅行肢藐,我們還可能出差等等故河,我們?cè)趯?shí)現(xiàn)這些功能時(shí)我們都需要提前準(zhǔn)備好調(diào)用者所需的依賴

有沒什么辦法可以改進(jìn)上面兩點(diǎn)呢?

服務(wù)容器(IOC容器)

這時(shí)服務(wù)容器就出現(xiàn)了吆豹,我們將上述例子中的交通工具鱼的,用餐,住宿統(tǒng)稱為服務(wù)痘煤,將服務(wù)統(tǒng)一收納起來凑阶,存放的地方就是服務(wù)容器,可以簡(jiǎn)單理解為在框架啟動(dòng)時(shí)將以上服務(wù)提前存入服務(wù)容器中的數(shù)組中衷快,在使用時(shí)去數(shù)組中獲取宙橱,下面進(jìn)行簡(jiǎn)單的實(shí)現(xiàn)

//-------------框架啟動(dòng)開始-------------
//服務(wù)容器
class Container
{
    //存放位置
    protected $bindings;
    //對(duì)服務(wù)進(jìn)行綁定
    public function bind($abstract, $concrete)
    {
        $this->bindings[$abstract] = $concrete;
    }
    //制造服務(wù)
    public function make($abstract)
    {
        return $this->bindings[$abstract];
    }
}
//創(chuàng)建容器
$container = new Container();
//以閉包的形式(后續(xù)會(huì)將為何閉包)注冊(cè)服務(wù)
$container->bind('tool', function(){
    //以汽車為例
    return new Car();
});
//-------------框架啟動(dòng)結(jié)束-------------

class Travel
{
    //出發(fā)
    function go(Traffic $tool)
    {
        //出發(fā)
        $tool->go();
    }
}
//獲取交通工具工廠類
$tool = $container->make('tool');
$travel = new Travel();
//閉包在使用的時(shí)候要先進(jìn)行調(diào)用所以這里是$tool()而不是直接將$tool傳入
$travel->go($tool());

這樣我們只需要在框架啟動(dòng)過程中就將所有可能用到的服務(wù)進(jìn)行注冊(cè)即可,使用時(shí)再去容器中獲取
在上面在對(duì)服務(wù)進(jìn)行綁定的時(shí)候使用了閉包的寫法避免了在綁定的過程中直接對(duì)類進(jìn)行實(shí)例化蘸拔,這樣就減少了框架啟動(dòng)時(shí)的開銷
具體的原理在這篇文章有提到:PHP Closure::bind方法理解及使用

上面舉得例子只是對(duì)交通工具進(jìn)行注冊(cè)师郑,但如果進(jìn)行注冊(cè)的服務(wù)有很多就會(huì)有一大堆服務(wù)雜亂無章的堆積在框架啟動(dòng)時(shí)的地方

服務(wù)提供者

這時(shí)服務(wù)提供者就來了
服務(wù)容器是將具體的服務(wù)進(jìn)行注冊(cè)收納及解析使用,通常一些不集中的服務(wù)都是通過在框架各個(gè)位置進(jìn)行注冊(cè)调窍,而服務(wù)提供者則是將與自身相關(guān)或依賴的服務(wù)進(jìn)行集合來注冊(cè)宝冕,類似交通工具,住宿之類的服務(wù)都會(huì)創(chuàng)建與之相關(guān)的交通工具服務(wù)提供者陨晶,住宿服務(wù)提供者猬仁,在具體的服務(wù)提供者類內(nèi)部進(jìn)行集合注冊(cè)帝璧,和一些前置處理,好處就是相關(guān)的服務(wù)可以用放在一起進(jìn)行集中管理
有沒發(fā)現(xiàn)這樣又有點(diǎn)像是工廠模式了湿刽,創(chuàng)建相關(guān)的服務(wù)提供者就像創(chuàng)建相關(guān)的工廠一樣的烁。其實(shí)項(xiàng)目演變就是這樣我們拋棄了相對(duì)較小工廠模式選擇了較大的“超級(jí)工廠”服務(wù)容器,服務(wù)容器壯大的同時(shí)又需要服務(wù)提供者來進(jìn)行分類管理诈闺。
下面進(jìn)行簡(jiǎn)單實(shí)現(xiàn)

//交通工具服務(wù)提供者
class ToolServiceProvider
{
    //統(tǒng)一注冊(cè)
    public function register()
    {
        //創(chuàng)建容器
        $container = new Container();
        $container->bind('tool', function () {
            //以汽車為例
            return new Car();
        });
    }
}
//交通工具服務(wù)提供者
$toolServiceProvider = new ToolServiceProvider();
$toolServiceProvider->register();
//用餐服務(wù)提供者(省略代碼未貼具體實(shí)現(xiàn)代碼)
$eatServiceProvider = new EatServiceProvider();
$eatServiceProvider->register();
//-------------框架啟動(dòng)結(jié)束-------------

class Travel
{
    //出發(fā)
    function go(Traffic $tool)
    {
        //出發(fā)
        $tool->go();
    }
}
//獲取交通工具工廠類
$tool = $container->make('tool');
$travel = new Travel();
$travel->go($tool);

本篇文章只是進(jìn)行思想解讀渴庆,上述所有代碼實(shí)現(xiàn)在實(shí)際運(yùn)用場(chǎng)景中都不僅僅是這么簡(jiǎn)單,特別是服務(wù)容器與服務(wù)提供者雅镊,在Laravel框架底層中的實(shí)現(xiàn)過程還是較為復(fù)雜的襟雷,但在使用方面還是十分優(yōu)雅,不會(huì)像上面的例子為了節(jié)省代碼顯得有點(diǎn)生搬硬套仁烹。

END

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
禁止轉(zhuǎn)載耸弄,如需轉(zhuǎn)載請(qǐng)通過簡(jiǎn)信或評(píng)論聯(lián)系作者。
  • 序言:七十年代末卓缰,一起剝皮案震驚了整個(gè)濱河市计呈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌征唬,老刑警劉巖捌显,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異总寒,居然都是意外死亡扶歪,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門摄闸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來善镰,“玉大人,你說我怎么就攤上這事贪薪∠苯” “怎么了眠副?”我有些...
    開封第一講書人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵画切,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我囱怕,道長(zhǎng)霍弹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任娃弓,我火速辦了婚禮典格,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘台丛。我一直安慰自己耍缴,他們只是感情好砾肺,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著防嗡,像睡著了一般变汪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蚁趁,一...
    開封第一講書人閱讀 51,155評(píng)論 1 299
  • 那天裙盾,我揣著相機(jī)與錄音,去河邊找鬼他嫡。 笑死番官,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的钢属。 我是一名探鬼主播徘熔,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼淆党!你這毒婦竟也來了近顷?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤宁否,失蹤者是張志新(化名)和其女友劉穎窒升,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體慕匠,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡饱须,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了台谊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蓉媳。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖锅铅,靈堂內(nèi)的尸體忽然破棺而出酪呻,到底是詐尸還是另有隱情,我是刑警寧澤盐须,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布玩荠,位于F島的核電站,受9級(jí)特大地震影響贼邓,放射性物質(zhì)發(fā)生泄漏阶冈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一塑径、第九天 我趴在偏房一處隱蔽的房頂上張望女坑。 院中可真熱鬧,春花似錦统舀、人聲如沸匆骗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽碉就。三九已至枢泰,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間铝噩,已是汗流浹背衡蚂。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留骏庸,地道東北人毛甲。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像具被,于是被迫代替她去往敵國和親玻募。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容