<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                ## **簡介** 容器,字面上理解就是裝東西的東西,常見的變量、對象屬性等都可以算是容器。一個容器能夠裝什么,全部取決于你對該容器的定義。至于IoC容器——Laravel的核心(服務容器),它存放的不是文本、數值,而是對象、對象的描述(類、接口)或者是提供對象的回調,通過這種容器,我們得以實現許多高級的功能,比如 “解耦” 、“依賴注入(DI)”。 ## **IoC容器誕生的故事** 從前的世界怪物橫行,我們需要超人用超能力來維護世界和平。 我們把一個“超人”作為一個類`class Superman {}` ,可以想像,一個超人誕生的時候肯定擁有至少一個超能力,這個超能力也可以抽象為一個對象,為這個對象定義一個描述他的類吧。一個超能力肯定有多種屬性、(操作)方法,這個可盡情的想象: ~~~ class Power { /** * 能力值 */ protected $ability; /** * 能力范圍或距離 */ protected $range; public function __construct($ability, $range) { $this->ability = $ability; $this->range = $range; } } ~~~ 這時候我們回過頭,修改一下之前的“超人”類,讓一個“超人”創建的時候被賦予一個超能力: ~~~ class Superman { protected $power; public function __construct() { $this->power = new Power(999, 100); } } ~~~ 這樣的話,當我們創建一個“超人”實例的時候,同時也創建了一個“超能力”的實例,但是,我們看到了一點,“超人”和“超能力”之間不可避免的產生了一個依賴。 在一個貫徹面向對象編程的項目中,這樣的依賴隨處可見。少量的依賴并不會有太過直觀的影響,我們隨著這個例子逐漸鋪開,讓大家慢慢意識到,當依賴達到一個量級時,是怎樣一番噩夢般的體驗。 ### **一堆亂麻——可怕的依賴** 在上例中,超能力被實例化為一個具體的超能力,但我們知道,超人的超能力是多元化的,每種超能力的方法、屬性都有不小的差異,沒法通過一種類描述完全。我們假設超人可以有以下多種超能力: * 飛行,屬性有:飛行速度、持續飛行時間 * 蠻力,屬性有:力量值 * 能量彈,屬性有:傷害值、射擊距離、同時射擊個數 我們創建了如下類: ~~~ class Flight { protected $speed; protected $holdtime; public function __construct($speed, $holdtime) {} } class Force { protected $force; public function __construct($force) {} } class Shot { protected $atk; protected $range; protected $limit; public function __construct($atk, $range, $limit) {} } //為了省事兒我沒有詳細寫出`__construct()`這個構造函數的全部,只寫了需要傳遞的參數 ~~~ 好了,這下我們的超人有點“忙”了。在超人初始化的時候,我們會根據需要來實例化其擁有的超能力嗎,大致如下: ~~~ class Superman { protected $power; public function __construct() { $this->power = new Fight(9, 100); // $this->power = new Force(45); // $this->power = new Shot(99, 50, 2); /* $this->power = array( new Force(45), new Shot(99, 50, 2) ); */ } } ~~~ 我們需要自己手動的在構造函數內(或者其他方法里)實例化一系列需要的類,這樣并不好。可以想象,假如需求變更(不同的怪物橫行地球),需要更多的有針對性的新的超能力,或者需要變更超能力的方法,我們必須 重新改造 超人。換句話說就是,改變超能力的同時,我還得重新制造個超人。 這時,靈機一動的人想到:為什么不可以這樣呢?超人的能力可以被隨時更換,只需要添加或者更新一個芯片或者其他裝置啥的(想到鋼鐵俠沒)。這樣的話就不要整個重新來過了。 也就是說,我們不再在“超人”類中固化了它的“超能力”初始化的行為,而轉由外部負責,由外部創造超能力模組、裝置或者芯片等(我們后面統一稱為 “模組”),植入超人體內的某一個接口,這個接口是一個既定的,只要這個 “模組” 滿足這個接口的裝置都可以被超人所利用,可以提升、增加超人的某一種能力。**這種由外部負責其依賴需求的行為,我們可以稱其為 “控制反轉(IoC)”。** ## **工廠模式,依賴轉移** 我們可以想到,組件、工具(或者超人的模組),是一種可被生產的玩意兒,生產的地方當然是 “工廠(Factory)”,于是有人就提出了這樣一種模式: 工廠模式。 工廠模式,顧名思義,就是一個類所依賴的外部事物的實例,都可以被一個或多個 “工廠” 創建的這樣一種開發模式,就是 “工廠模式”。 我們為了給超人制造超能力模組,我們創建了一個工廠,它可以制造各種各樣的模組,且僅需要通過一個方法: ~~~ class SuperModuleFactory { public function makeModule($moduleName, $options) { switch ($moduleName) { case 'Fight': return new Fight($options[0], $options[1]); case 'Force': return new Force($options[0]); case 'Shot': return new Shot($options[0], $options[1], $options[2]); } } } ~~~ 然后,超人 創建之初就可以使用這個工廠! ~~~ class Superman { protected $power; public function __construct() { // 初始化工廠 $factory = new SuperModuleFactory; // 通過工廠提供的方法制造需要的模塊 $this->power = $factory->makeModule('Fight', [9, 100]); // $this->power = $factory->makeModule('Force', [45]); // $this->power = $factory->makeModule('Shot', [99, 50, 2]); /* $this->power = array( $factory->makeModule('Force', [45]), $factory->makeModule('Shot', [99, 50, 2]) ); */ } } ~~~ 可以看得出,我們不再需要在超人初始化之初,去初始化許多第三方類,只需初始化一個工廠類,即可滿足需求。但這樣似乎和以前區別不大,只是沒有那么多 new 關鍵字。其實我們稍微改造一下這個類,你就明白,工廠類的真正意義和價值了。 ~~~ class Superman { protected $power; public function __construct(array $modules) { // 初始化工廠 $factory = new SuperModuleFactory; // 通過工廠提供的方法制造需要的模塊 foreach ($modules as $moduleName => $moduleOptions) { $this->power[] = $factory->makeModule($moduleName, $moduleOptions); } } } // 創建超人 $superman = new Superman([ 'Fight' => [9, 100], 'Shot' => [99, 50, 2] ]); ~~~ 現在修改的結果令人滿意。現在,“超人” 的創建不再依賴任何一個 “超能力” 的類,我們如若修改了或者增加了新的超能力,只需要針對修改 SuperModuleFactory 即可。擴充超能力的同時不再需要重新編輯超人的類文件,使得我們變得很輕松。但是,這才剛剛開始。 ## **IoC容器的重要組成——依賴注入** 由 “超人” 對 “超能力” 的依賴變成 “超人” 對 “超能力模組工廠” 的依賴后,對付小怪獸們變得更加得心應手。但這也正如你所看到的,依賴并未解除,只是由原來對多個外部的依賴變成了對一個 “工廠” 的依賴。假如工廠出了點麻煩,問題變得就很棘手。 > 其實大多數情況下,工廠模式已經足夠了。工廠模式的缺點就是:接口未知(即沒有一個很好的契約模型)、產生對象類型單一。總之就是,還是不夠靈活(為了講解后面的依賴注入 ,這里就先夸大一下工廠模式的缺陷)。 我們知道,超人依賴的模組,我們要求有統一的接口,這樣才能和超人身上的注入接口對接,最終起到提升超能力的效果。 但由于工廠模式下,所有的模組都已經在工廠類中安排好了(生產能力受限),如果有新的、高級的模組加入,我們必須修改工廠類(好比增加新的生產線): ~~~ class SuperModuleFactory { public function makeModule($moduleName, $options) { switch ($moduleName) { case 'Fight': return new Fight($options[0], $options[1]); case 'Force': return new Force($options[0]); case 'Shot': return new Shot($options[0], $options[1], $options[2]); // case 'more': ....... // case 'and more': ....... // case 'and more': ....... // case 'oh no! its too many!': ....... } } } ~~~ 由于對超能力模組的需求不斷增大,我們需要集合整個世界的高智商人才,一起解決問題,不應該僅僅只有幾個工廠壟斷負責。不過高智商人才們都非常自負,認為自己的想法是對的,創造出的超能力模組沒有統一的接口,自然而然無法被正常使用。這時我們需要提出一種契約,這樣無論是誰創造出的模組,都符合這樣的接口,自然就可被正常使用。 ~~~ interface SuperModuleInterface { /** *超能力激活方法 *任何一個超能力都得有該方法,并擁有一個參數 *@param array $target 針對目標,可以是一個或多個,自己或他人 **/ public function activate(array $target); } ~~~ 上文中,我們定下了一個接口 (超能力模組的規范、契約),所有被創造的模組必須遵守該規范,才能被生產。 這時候,那些提出更好的超能力模組的高智商人才,遵循這個接口,創建了下述(模組)類: ~~~ /** * 天生能量工廠 */ class XPower implements SuperModuleInterface { public function activate(array $target) { // 這只是個例子。。具體自行腦補 } } /** * 外力武器工廠 */ class UltraBomb implements SuperModuleInterface { public function activate(array $target) { // 這只是個例子。。具體自行腦補 } } ~~~ 改造完畢!現在,當我們初始化 “超人” 類的時候,提供的模組實例必須是一個`SuperModuleInterface`接口的實現。否則就會提示錯誤。 ### **什么叫做依賴注入** 本文從開頭到現在提到的一系列依賴,只要不是由內部生產(比如初始化、構造函數 \_\_construct 中通過工廠方法、自行手動 new 的),而是由外部以參數或其他形式注入的,都屬于依賴注入(DI) 。是不是豁然開朗?事實上,就是這么簡單。下面就是一個典型的依賴注入: ~~~ // 超能力模組 $superModule = new XPower; // 初始化一個超人,并注入一個超能力模組依賴 $superMan = new Superman($superModule); ~~~ 關于依賴注入這個本文的主要配角,也就這么多需要講的。理解了依賴注入,我們就可以繼續深入問題。慢慢走近今天的主角…… ## **更為先進得工廠——IoC容器** 剛剛初始化一個超人時,我們手動創建了一個超能力模組,手動創建超人并注入了剛剛創建超能力得模組。so,手動?。。。 一群怪獸來了,如此低效率產出超人是不現實,我們需要自動化 —— 最多一條指令,千軍萬馬來相見。我們需要一種高級的生產車間,我們只需要向生產車間提交一個腳本,工廠便能夠通過指令自動化生產。這種更為高級的工廠,就是工廠模式的升華 —— IoC 容器。 ~~~ class Container { protected $binds; protected $instances; public function bind($abstract, $concrete) { if ($concrete instanceof Closure) { $this->binds[$abstract] = $concrete; } else { $this->instances[$abstract] = $concrete; } } public function make($abstract, $parameters = []) { if (isset($this->instances[$abstract])) { return $this->instances[$abstract]; } array_unshift($parameters, $this); return call_user_func_array($this->binds[$abstract], $parameters); } } ~~~ 這時候,一個十分粗糙的容器就誕生了。現在的確很簡陋,但不妨礙我們進一步提升他。先著眼現在,看看這個容器如何使用吧! ~~~ // 創建一個容器(后面稱作超級工廠) $container = new Container; // 向該 超級工廠添加超人的生產腳本 $container->bind('superman', function($container, $moduleName) { return new Superman($container->make($moduleName)); }); // 向該 超級工廠添加超能力模組的生產腳本 $container->bind('xpower', function($container) { return new XPower; }); // 同上 $container->bind('ultrabomb', function($container) { return new UltraBomb; }); // ****************** 華麗麗的分割線 ********************** // 開始啟動生產 $superman_1 = $container->make('superman', 'xpower'); $superman_2 = $container->make('superman', 'ultrabomb'); $superman_3 = $container->make('superman', 'xpower'); // ...隨意添加 ~~~ 看到沒?通過最初的 綁定(`bind`) 操作,我們向 超級工廠 注冊了一些生產腳本,這些生產腳本在生產指令下達之時便會執行。發現沒有?我們徹底的解除了 超人 與 超能力模組 的依賴關系,更重要的是,容器類也絲毫沒有和他們產生任何依賴!我們通過注冊、綁定的方式向容器中添加一段可以被執行的回調(可以是匿名函數、非匿名函數、類的方法)作為生產一個類的實例的 腳本 ,只有在真正的 生產(`make`) 操作被調用執行時,才會觸發。 這樣一種方式,使得我們更容易在創建一個實例的同時解決其依賴關系,并且更加靈活。當有新的需求,只需另外綁定一個“生產腳本”即可。 實際上,真正的 IoC 容器更為高級。我們現在的例子中,還是需要手動提供超人所需要的模組參數,但真正的 IoC 容器會根據類的依賴需求,自動在注冊、綁定的一堆實例中搜尋符合的依賴需求,并自動注入到構造函數參數中去。Laravel 框架的服務容器正是這么做的。(這種自動搜尋依賴需求的功能,是通過[反射(Reflection)](http://php.net/manual/zh/book.reflection.php)實現的,恰好的,php 完美的支持反射機制! 現在,到目前為止,我們已經不再懼怕怪獸們了。高智商人才集思廣益,井井有條,根據接口契約創造規范的超能力模組。超人開始批量產出。最終,人人都是超人,你也可以是哦! ## **重新審視Laravel的核心** 現在,我們開始慢慢解讀 Laravel 的核心。其實,Laravel 的核心就是一個 IoC 容器,也恰好是我之前所說的高級的 IoC 容器。 Laravel 的核心本身十分輕量,并沒有什么很神奇很實質性的應用功能。很多人用到的各種功能模塊比如 Route(路由)、Eloquent ORM(數據庫 ORM 組件)、Request(請求)以及?Response(響應)等等等等,實際上都是與核心無關的類模塊提供的,這些類從注冊到實例化,最終被你所使用,其實都是 Laravel 的服務容器負責的。 我們以大家最常見的 Route 類作為例子。大家可能經常見到路由定義是這樣的: ~~~ Route::get('/', function() { // bla bla bla... }); ~~~ 實際上, Route 類被定義在這個命名空間:`Illuminate\Routing\Router`,文件`vendor/laravel/framework/src/Illuminate/Routing/Router.php`。 我們通過打開發現,這個類的這一系列方法,如`get`,`post`,`any`等都不是靜態(`static`)方法,這是怎么一回事兒?不要急,我們繼續。 ### **服務提供者** 我們在前文介紹 IoC 容器的部分中,提到了,一個類需要綁定、注冊至容器中,才能被“制造”。也就是說,讓我們需要某個服務時,就得先注冊、綁定這個服務到容器,那么提供服務并綁定服務至容器的東西,就是服務提供者(Service Provider)。 雖然綁定一個類到容器不一定非要通過服務提供者,但我們知道,有時候我們的類、模塊會有需要其他類和組件的情況,為了保證初始化階段不會出現所需要的模塊和組件沒有注冊的情況,Laravel 將注冊和初始化行為進行拆分,注冊的時候就只能注冊,初始化的時候就是初始化。拆分后的產物就是現在的服務提供者。 服務提供者主要分為兩個部分,`register`(注冊) 和`boot`(引導、初始化),具體參考[文檔](https://xueyuanjun.com/post/91.html)。`register`負責進行向容器注冊“腳本”,但要注意注冊部分不要有對未知事物的依賴,如果有,就要移步至`boot`部分。 ## **門面(Facade)** 我們現在解答之前關于 Route 的方法為何能以靜態方法訪問的問題。簡單說來就是模擬一個類,提供一個靜態魔術方法`__callStatic`,并將該靜態方法映射到真正的方法上。 我們使用的 Route 類實際上是`Illuminate\Support\Facades\Route`通過`class_alias()`函數創造的別名而已,這個類被定義在文件`vendor/laravel/framework/src/Illuminate/Support/Facades/Route.php`。 我們打開文件一看……誒?怎么只有這么簡單的一段代碼呢? ~~~ <?php namespace Illuminate\Support\Facades; /** * @see \Illuminate\Routing\Router */ class Route extends Facade { /** * Get the registered name of the component. * * @return string */ protected static function getFacadeAccessor() { return 'router'; } } ~~~ 上述簡單的定義中,我們看到這個類繼承了一個叫做`Facade`的類,然后`getFacadeAccessor`方法返回了一個`route`,這是什么意思呢?事實上,這個值被一個`ServiceProvider`注冊過,大家應該知道注冊了個什么,當然是那個真正的路由類!
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看