摘要:的優美得益于開發的組件式解耦,這與服務容器和服務提供者的理念是離不開的,下篇將用框架中類來梳理服務容器的工作流程。
原文:blog,轉載注明來源即可。
本文代碼:GitHub
服務容器是 Laravel 框架實現模塊化解耦的核心。模塊化即是將系統拆成多個子模塊,子模塊間的耦合程度盡可能的低,代碼中盡可能的避免直接調用。這樣才能提高系統的代碼重用性、可維護性、擴展性。
下邊出行例子有火車、飛機兩種出行方式,對應給出了 3 種耦合度越來越低的實現:高度耦合實現、工廠模式解耦、IOC 模式解耦。
高度耦合實現 代碼實現定義 TrafficTool 接口并用 Train、Plane 實現,最后在 Traveler 中實例化出行工具后說走就走。代碼十分簡潔:
travelTool = new Train(); } public function travel() { $this->travelTool->go(); } } $me = new Traveler(); $me->travel();
運行:
$ php normal.php [Travel By]: train優點
代碼十分簡潔:一個接口兩個類最后直接調用。
缺點在第 32 行,Traveler 與 Train 兩個組件發生了耦合。以后想坐飛機出行,必須修改 __construct() 的內部實現:$this->travelTool = new Plane();
重用性和可維護性都很差:在實際的軟件開發中,代碼會根據業務需求的變化而不斷修改。如果組件之間直接相互調用,那組件的代碼就不能輕易修改,以免調用它的地方出現錯誤。
工廠模式解耦 工廠模式分離代碼中不變和變的部分,使得在不同條件下創建不同的對象。
代碼實現... class TrafficToolFactory { public function create($name) { switch ($name) { case "train": return new Train(); case "plane": return new Plane(); default: exit("[No Traffic Tool] :" . $name); } } } // 旅游者類,使用火車出行 class Traveler { protected $trafficTool; public function __construct($toolName) { // 使用工廠類實例化需要的交通工具 $factory = new TrafficToolFactory(); $this->travelTool = $factory->create($toolName); } public function travel() { $this->travelTool->go(); } } // 傳入指定的方式 $me = new Traveler("train"); $me->travel();
運行:
$ php factory.php [Travel By]: train優點
提取了代碼中變化的部分:更換交通工具,坐飛機出行直接修改 $me = new Traveler("plane") 即可。適用于需求簡單的情況。
缺點依舊沒有徹底解決依賴:現在 Traveler 與 TrafficToolFactory 發生了依賴。當需求增多后,工廠的 switch...case 等代碼也不易維護。
IOC 模式解耦IOC 是 Inversion Of Controll 的縮寫,即控制反轉。這里的“反轉”可理解為將組件間依賴關系提到外部管理。
簡單的依賴注入依賴注入是 IOC 的一種實現方式,是指組件間的依賴通過外部參數(interface)形式直接注入。比如對上邊的工廠模式進一步解耦:
trafficTool = $tool; } public function travel() { $this->trafficTool->go(); } } $train = new Train(); $me = new Traveler($train); // 將依賴直接以參數的形式注入 $me->travel();
運行:
$ php simple_ioc.php [Travel By]: train高級依賴注入 簡單注入的問題
如果三個人分別自駕游、坐飛機、高鐵出去玩,那你的代碼可能是這樣的:
$train = new Train(); $plane = new Plane(); $car = new Car(); $a = new Traveler($car); $b = new Traveler($plane); $c = new Traveler($train); $a->travel(); $b->travel(); $c->travel();
看起來就兩個字:藍瘦。上邊簡單的依賴注入相比工廠模式已經解耦挺多了,參考 Laravel 中服務容器的概念,還能繼續解耦。將會使用到 PHP 反射和匿名函數,參考:Laravel 框架中常用的 PHP 語法
IOC 容器高級依賴注入 = 簡單依賴注入 + IOC 容器
binds[$abstract] = $concrete; } else { $this->instances[$abstract] = $concrete; } } /** * 生產:執行回調函數 * * @param $abstract 字符指令 * @param array $params 回調函數所需參數 * @return mixed 回調函數的返回值 */ public function make($abstract, $params = []) { if (isset($this->instances[$abstract])) { return $this->instances[$abstract]; } // 此時 $this 是有 2 個元素的數組 // Array ( // [0] => Container Object ( // [binds] => Array ( ... ) // [instances] => Array() // ) // [1] => "train" // ) array_unshift($params, $this); // 將參數傳遞給回調函數 return call_user_func_array($this->binds[$abstract], $params); } } $container = new Container(); $container->bind("traveler", function ($container, $trafficTool) { return new Traveler($container->make($trafficTool)); }); $container->bind("train", function ($container) { return new Train(); }); $container->bind("plane", function ($container) { return new Plane(); }); $me = $container->make("traveler", ["train"]); $me->travel();
運行:
$ php advanced_ioc.php [Travel By]: train簡化并解耦后的代碼
那三個人再出去玩,代碼將簡化為:
$a = $container->make("traveler", ["car"]); $b = $container->make("traveler", ["train"]); $c = $container->make("traveler", ["plane"]); $a->travel(); $b->travel(); $c->travel();
更多參考:神奇的服務容器
Laravel 的服務容器Laravel 自己的服務容器是一個更加高級的 IOC 容器,它的簡化代碼如下:
getClosure($abstract, $concrete); } $this->binds[$abstract] = compact("concrete", "shared"); } // 獲取回調函數 public function getClosure($abstract, $concrete) { return function ($container) use ($abstract, $concrete) { $method = ($abstract == $concrete) ? "build" : "make"; return $container->$method($concrete); }; } protected function getConcrete($abstract) { if (!isset($this->binds[$abstract])) { return $abstract; } return $this->binds[$abstract]["concrete"]; } // 生成實例對象 public function make($abstract) { $concrete = $this->getConcrete($abstract); if ($this->isBuildable($abstract, $concrete)) { $obj = $this->build($concrete); } else { $obj = $this->make($concrete); } return $obj; } // 判斷是否要用反射來實例化 protected function isBuildable($abstract, $concrete) { return $concrete == $abstract || $concrete instanceof Closure; } // 通過反射來實例化 $concrete 的對象 public function build($concrete) { if ($concrete instanceof Closure) { return $concrete($this); } $reflector = new ReflectionClass($concrete); if (!$reflector->isInstantiable()) { echo "[can"t instantiable]: " . $concrete; } $constructor = $reflector->getConstructor(); // 使用默認的構造函數 if (is_null($constructor)) { return new $concrete; } $refParams = $constructor->getParameters(); $instances = $this->getDependencies($refParams); return $reflector->newInstanceArgs($instances); } // 獲取實例化對象時所需的參數 public function getDependencies($refParams) { $deps = []; foreach ($refParams as $refParam) { $dep = $refParam->getClass(); if (is_null($dep)) { $deps[] = null; } else { $deps[] = $this->resolveClass($refParam); } } return (array)$deps; } // 獲取參數的類型類名字 public function resolveClass(ReflectionParameter $refParam) { return $this->make($refParam->getClass()->name); } } $container = new Container(); // 將 traveller 對接到 Train $container->bind("TrafficTool", "Train"); $container->bind("traveller", "Traveller"); // 創建 traveller 實例 $me = $container->make("traveller"); $me->travel();
運行:
$ php laravel_ioc.php [Travel By]: train
Train 類要能被實例化,需要先注冊到容器,這就涉及到 Laravel 中服務提供者(Service Provider)的概念了。至于服務提供者是怎么注冊類、注冊之后如何實例化、實例化后如何調用的... 下節詳細分析。
總結本文用一個旅游出行的 demo,引出了高度耦合的直接實現、工廠模式解耦和 IOC 模式解耦共計三種實現方式,越往后代碼量越多還有些繞,但類(模塊)之間的耦合度越來越低,最后實現了簡化版的 Laravel 服務容器。
Laravel 的優美得益于開發的組件式解耦,這與服務容器和服務提供者的理念是離不開的,下篇將用 Laravel 框架 laravel/framework/src/Illuminate/Container.php 中 Container 類來梳理 Laravel 服務容器的工作流程。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/28798.html
摘要:可以為服務提供者的方法設置類型提示。方法將在所有其他服務提供者均已注冊之后調用。所有服務提供者都在配置文件中注冊。可以選擇推遲服務提供者的注冊,直到真正需要注冊綁定時,這樣可以提供應用程序的性能。 本文最早發布于 Rootrl的Blog 導言 Laravel是一款先進的現代化框架,里面有一些概念非常重要。在上手Laravel之前,我認為先弄懂這些概念是很有必要的。你甚至需要重溫下PHP...
摘要:依賴注入依賴注入一詞是由提出的術語,它是將組件注入到應用程序中的一種行為。就像說的依賴注入是敏捷架構中關鍵元素。類依賴于,所以我們的代碼可能是這樣的創建一個這是一種經典的方法,讓我們從使用構造函數注入開始。 showImg(https://segmentfault.com/img/remote/1460000018806800); 文章轉自:https://learnku.com/la...
摘要:劃下重點,服務容器是用于管理類的依賴和執行依賴注入的工具。類的實例化及其依賴的注入,完全由服務容器自動的去完成。 本文首發于 深入剖析 Laravel 服務容器,轉載請注明出處。喜歡的朋友不要吝嗇你們的贊同,謝謝。 之前在 深度挖掘 Laravel 生命周期 一文中,我們有去探究 Laravel 究竟是如何接收 HTTP 請求,又是如何生成響應并最終呈現給用戶的工作原理。 本章將帶領大...
摘要:工廠模式,依賴轉移當然,實現控制反轉的方法有幾種。其實我們稍微改造一下這個類,你就明白,工廠類的真正意義和價值了。雖然如此,工廠模式依舊十分優秀,并且適用于絕大多數情況。 此篇文章轉載自laravel-china,chongyi的文章https://laravel-china.org/top...原文地址: http://www.insp.top/learn-lar... ,轉載務必保...
摘要:本文一大半內容都是通過舉例來讓讀者去理解什么是控制反轉和依賴注入,通過理解這些概念,來更加深入。這種由外部負責其依賴需求的行為,我們可以稱其為控制反轉。工廠模式,依賴轉移當然,實現控制反轉的方法有幾種。 容器,字面上理解就是裝東西的東西。常見的變量、對象屬性等都可以算是容器。一個容器能夠裝什么,全部取決于你對該容器的定義。當然,有這樣一種容器,它存放的不是文本、數值,而是對象、對象的描...
閱讀 3152·2021-11-24 10:24
閱讀 2957·2021-11-11 16:54
閱讀 3083·2021-09-22 15:55
閱讀 2037·2019-08-30 15:44
閱讀 1908·2019-08-29 18:41
閱讀 2770·2019-08-29 13:43
閱讀 3061·2019-08-29 12:51
閱讀 1194·2019-08-26 12:19