摘要:在開始之前要明確一個概念不管是設計模式還是依賴注入等等都是為了實現(xiàn)模塊化所謂模塊化就是希望一個軟件是由很多子模塊組成的這些模塊之間的依賴程度盡量的低也就是如果系統(tǒng)中不需要某一個功能那么只要移除這個功能所對應的模塊就可以了那么我們今天要說的服
在開始之前要明確一個概念,不管是設計模式,還是依賴注入等等,都是為了實現(xiàn)模塊化.所謂模塊化就是希望一個軟件是由很多子模塊組成的,這些模塊之間的依賴程度盡量的低,也就是如果系統(tǒng)中不需要某一個功能,那么只要移除這個功能所對應的模塊就可以了.
那么,我們今天要說的服務容器就是為了實現(xiàn)上面的功能.你應該聽過,Laravel中的服務容器其本質(zhì)上是一個IoC容器,但是好像隊IoC又不是很了解,講來講去優(yōu)點很多,功能很強勁.但是不懂原理怎么用都不踏實啊.所以,這里我們自己來實現(xiàn)一個IoC容器,洞察其本質(zhì).
在開始之前,先說明一點,閱讀本篇文章至少要保證有一下的基礎知識:
php反射用法
閉包的use用法
如果不懂上面的內(nèi)容,請先補充.避免閱讀代碼時候產(chǎn)生的不適感.
getClosure($abstract, $concrete); } $this->binding[$abstract] = compact("concrete", "shared"); } protected function getClosure($abstract, $concrete) { return function ($c) use ($abstract, $concrete) { $method = ($abstract == $concrete) ? "build" : "make"; return $c->$method($concrete); }; } /** * @param $abstract * @return object * */ public function make($abstract) { $concrete = $this->getConcrete($abstract); if ($this->isBuildable($concrete, $abstract)) { $object = $this->build($concrete); } else { $object = $this->make($concrete); } return $object; } /** * @param $concrete * @param $abstract * @return bool */ public function isBuildable($concrete, $abstract) { return $concrete === $abstract || $concrete instanceof Closure; } /** * @param $abstract * @return mixed */ protected function getConcrete($abstract) { if (!isset($this->binding[$abstract])) { return $abstract; } return $this->binding[$abstract]["concrete"]; } /** * @param $concrete * @return object */ public function build($concrete) { if($concrete instanceof Closure) { return $concrete($this); } //反射... $reflector = new ReflectionClass($concrete); if(!$reflector->isInstantiable()) { echo $message = "Target [$concrete] is not instantiable"; } //獲取要實例化對象的構造函數(shù) $constructor = $reflector->getConstructor(); //沒有定義構造函數(shù),只有默認的構造函數(shù),說明構造函數(shù)參數(shù)個數(shù)為空 if(is_null($constructor)) { return new $concrete; } //獲取構造函數(shù)所需要的所有參數(shù) $dependencies = $constructor->getParameters(); $instances = $this->getDependencies($dependencies); //從給出的數(shù)組參數(shù)在中實例化對象 return $reflector->newInstanceArgs($instances); } /** * @param $paramters * @return array * 獲取構建類所需要的所有依賴,級構造函數(shù)所需要的參數(shù) , */ protected function getDependencies($paramters) { $dependencies = []; foreach ($paramters as $paramter) { //獲取到參數(shù)名稱. $dep = $paramter->getClass(); if(is_null($dep)){ $dependencies = null; }else{ $dependencies[] = $this->resolveClass($paramter); } } return (array)$dependencies; } /** * @param ReflectionParameter $parameter * @return object * 實例化 構造函數(shù)中所需要的參數(shù). */ protected function resolveClass(ReflectionParameter $parameter) { $name = $parameter->getClass()->name; return $this->make($name); } }
這就是一個IoC容器的實現(xiàn)代碼.乍一看,很麻煩.其實真的蠻麻煩的 =_=,如果是第一次接觸的話,并不是那么好消化,這里再給出使用IoC容器的代碼
_trafficTool = $trafficTool; } public function visitTibet() { $this->_trafficTool->go(); } } //實例化IoC容器 $app = new Container(); //綁定某一功能到IoC $app->bind("TrafficTool", "Train"); $app->bind("travellerA", "Traveller"); // 實例化對象 $tra = $app->make("travellerA"); $tra->visitTibet();
運行例子發(fā)現(xiàn)會輸出:train...這個例子假設旅行者去青藏旅行,可以坐火車(train)或者走路(leg)去青藏.
好了,其實這樣子本篇文章就可以結(jié)束了,因為所有的答案都在IoC容器的實現(xiàn)中, 但是為了可以更好的理解上面的代碼,我們繼續(xù)往下分析.
首先,希望你可以運行一下上面的代碼,雖然簡單的運行代碼并不會幫助你理解代碼,但是一個可以運行的例子會讓人比較踏實,能夠更有把握的理解代碼.
在深入每一行代碼之前,我們從整體上來分析,IoC解決了一個什么問題?簡單點說,就是我們再實例化對象的時候不用使用new了,有了IoC容器之后,我們調(diào)用make函數(shù)就可以實例化出一個對象了.然而,你發(fā)現(xiàn),Traveller的構造函數(shù)是需要一個參數(shù)的,可是我們好像并沒有提供這個參數(shù)?
這就是IoC強大之處了, 調(diào)用make實例化對象的時候,容器會使用反射功能,去分析我們要實例化對象的構造函數(shù),獲取構造函數(shù)所需的每個參數(shù),然后分別去實例化這些參數(shù),如果實例化這些參數(shù)也要參數(shù),那么就再去實例化參數(shù)的參數(shù).....=_=.到最后成功實例化我們所需要的traveller了.在Container的build函數(shù)就是使用反射來實例化對象.
但是,有一個問題了,IoC容器怎么知道實例化Traveller的時候需要的參數(shù)train,而不是leg?
其實,IoC容器什么都不知道,IoC會實例化哪些對象都是通過bind函數(shù)告訴IoC的,上面的例子兩次調(diào)用bind函數(shù),就是告訴Ioc可以實例化的對象有Train和Traveller. 再通俗講就是:當需要當我們需要TrafficTool這個服務的時候去實例化Train這個類,需要一個travellerA的旅行者的時候去實例化Traveller類.而Train這個就是travellerA就是去青藏的方式. 這樣子如果想要走路去青藏的話只要把$app->bind("Visit", "Train");改為$app->bind("Visit", "Leg");就可以.
可是,這上面的這些有什么意義?直接$tra = new Traveller($trafficTool)來實例化對象好像也沒有什么不好的.
使用new來實例化對象的時候,會產(chǎn)生依賴.比如上面$tra = new Traveller($trafficTool),這說明我們要創(chuàng)建一個Traveller之前得有一個$trafficTool,即Traveller依賴于trafficTool.當使用new來實例化Traveller的時候,Traveller和trafficTool之間就產(chǎn)生了耦合.這樣,這兩個組件就沒辦法分開了.
而使用IoC是怎么解決這個問題的,之前說過,如果想要如果想要走路去青藏的話只要把$app->bind("Visit", "Train");改為$app->bind("Visit", "Leg");就可以.這樣子,使用何種方式去青藏,我們可以自由的選擇.
我們站在Laravel框架設計者的角度去想,設計者肯定希望一個框架提供的功能越多越好,但是又要保證強大的同時又不會限制使用者.最好可以保證使用者想實現(xiàn)什么奇怪的需求都可以.那么功能強大但是又不局限的最好方法就是什么都不做,提供一個強大的IoC容器.所有需要實現(xiàn)的功能都變成一個個服務,需要什么服務就把服務注冊(即調(diào)用bind函數(shù))到IoC中,然后讓IoC去管理依賴.
開發(fā)者想到一個{{BANNED}}的需求:走路去青藏,那么只要你實現(xiàn)了走路去青藏這個功能,然后把這個功能當做一個服務注冊到IoC中,以后你需要這個服務的時候IoC就幫你實例化這個服務.當開發(fā)者回歸正常之后覺得還是坐火車去吧,于是不注冊走路這個功能,實現(xiàn)坐火車的功能,然后注冊這個功能.下次IoC實例化的時候就是實例化坐火車這個功能了.
好了,剩下的部分就是一行一行的閱讀Container的代碼了,Laravel框架中的服務容器代碼也是這個樣子,只是功能更加強悍.但是核心是一樣的,上面的代碼懂了以后再使用Laravel框架就會更加游刃有余了.
文章雖短.但是內(nèi)容很多.尤其是代碼,雖然可能只是短短的一個例子,但是包含了很多內(nèi)容.值得好好分析,這里放個彩蛋:Traveller中構造函數(shù)參數(shù)類似為TrafficTool,是一個接口.但是實例化的是Train.這里體現(xiàn)了設計模式的一個原則
面對接口編程,而不是面對實現(xiàn)編程.
文章版權歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/22140.html
摘要:劃下重點,服務容器是用于管理類的依賴和執(zhí)行依賴注入的工具。類的實例化及其依賴的注入,完全由服務容器自動的去完成。 本文首發(fā)于 深入剖析 Laravel 服務容器,轉(zhuǎn)載請注明出處。喜歡的朋友不要吝嗇你們的贊同,謝謝。 之前在 深度挖掘 Laravel 生命周期 一文中,我們有去探究 Laravel 究竟是如何接收 HTTP 請求,又是如何生成響應并最終呈現(xiàn)給用戶的工作原理。 本章將帶領大...
摘要:一個服務提供器必須包含至少一種方法。服務提供器一旦被注冊,就可被用于程序的各個地方。注意服務提供器的變量來自類中。啟動服務當所有的服務提供器注冊之后,他們就變成了已啟動狀態(tài)。再次提示,把服務提供器作為一種組織工具來使用。 聲明:本文并非博主原創(chuàng),而是來自對《Laravel 4 From Apprentice to Artisan》閱讀的翻譯和理解,當然也不是原汁原味的翻譯,能保證90%...
摘要:服務提供者啟動原理之前我們有學習深度挖掘生命周期和深入剖析服務容器,今天我們將學習服務提供者。的所有核心服務都是通過服務提供者進行引導啟動的,所以想深入了解那么研究服務提供者的原理是個繞不開的話題。 本文首發(fā)于 深入剖析 Laravel 服務提供者實現(xiàn)原理,轉(zhuǎn)載請注明出處。 今天我們將學習 Laravel 框架另外一個核心內(nèi)容「服務提供者(Service Provider)」。服務提供...
摘要:控制反轉(zhuǎn)容器控制反轉(zhuǎn)使依賴注入變得更加便捷。有瑕疵控制反轉(zhuǎn)容器是實現(xiàn)的控制翻轉(zhuǎn)容器的一種替代方案。容器的獨立使用即使沒有使用框架,我們?nèi)匀豢梢栽陧椖恐惺褂冒惭b組件來使用的控制反轉(zhuǎn)容器。在沒有給定任何信息的情況下,容器是無法實例化相關依賴的。 聲明:本文并非博主原創(chuàng),而是來自對《Laravel 4 From Apprentice to Artisan》閱讀的翻譯和理解,當然也不是原汁原味...
摘要:組件擴展通常有兩種方法向容器中綁定自己的接口實現(xiàn)痛過使用工廠模式實現(xiàn)的類注冊自己的擴展。類庫管理類以工廠模式實現(xiàn),負責諸如緩存等驅(qū)動的實例化。閉包須要傳入繼承自和容器的實例化對象。當完成擴展之后要記住中替換成自己的擴展名稱。 聲明:本文并非博主原創(chuàng),而是來自對《Laravel 4 From Apprentice to Artisan》閱讀的翻譯和理解,當然也不是原汁原味的翻譯,能保證9...
閱讀 2470·2023-04-25 21:41
閱讀 1647·2021-09-22 15:17
閱讀 1921·2021-09-22 10:02
閱讀 2433·2021-09-10 11:21
閱讀 2569·2019-08-30 15:53
閱讀 996·2019-08-30 15:44
閱讀 946·2019-08-30 13:46
閱讀 1125·2019-08-29 18:36