摘要:代碼這就是控制反轉(zhuǎn)模式。是變量有默認(rèn)值則設(shè)置默認(rèn)值是一個(gè)類,遞歸解析有默認(rèn)值則返回默認(rèn)值從容器中取得以上代碼的原理參考官方文檔反射,具有完整的反射,添加了對(duì)類接口函數(shù)方法和擴(kuò)展進(jìn)行反向工程的能力。
PHP程序員如何理解依賴注入容器(dependency injection container) 背景知識(shí)
傳統(tǒng)的思路是應(yīng)用程序用到一個(gè)Foo類,就會(huì)創(chuàng)建Foo類并調(diào)用Foo類的方法,假如這個(gè)方法內(nèi)需要一個(gè)Bar類,就會(huì)創(chuàng)建Bar類并調(diào)用Bar類的方法,而這個(gè)方法內(nèi)需要一個(gè)Bim類,就會(huì)創(chuàng)建Bim類,接著做些其它工作。
// 代碼【1】 class Bim { public function doSomething() { echo __METHOD__, "|"; } } class Bar { public function doSomething() { $bim = new Bim(); $bim->doSomething(); echo __METHOD__, "|"; } } class Foo { public function doSomething() { $bar = new Bar(); $bar->doSomething(); echo __METHOD__; } } $foo = new Foo(); $foo->doSomething(); //Bim::doSomething|Bar::doSomething|Foo::doSomething
使用依賴注入的思路是應(yīng)用程序用到Foo類,F(xiàn)oo類需要Bar類,Bar類需要Bim類,那么先創(chuàng)建Bim類,再創(chuàng)建Bar類并把Bim注入,再創(chuàng)建Foo類,并把Bar類注入,再調(diào)用Foo方法,F(xiàn)oo調(diào)用Bar方法,接著做些其它工作。
// 代碼【2】 class Bim { public function doSomething() { echo __METHOD__, "|"; } } class Bar { private $bim; public function __construct(Bim $bim) { $this->bim = $bim; } public function doSomething() { $this->bim->doSomething(); echo __METHOD__, "|"; } } class Foo { private $bar; public function __construct(Bar $bar) { $this->bar = $bar; } public function doSomething() { $this->bar->doSomething(); echo __METHOD__; } } $foo = new Foo(new Bar(new Bim())); $foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething
這就是控制反轉(zhuǎn)模式。依賴關(guān)系的控制反轉(zhuǎn)到調(diào)用鏈的起點(diǎn)。這樣你可以完全控制依賴關(guān)系,通過(guò)調(diào)整不同的注入對(duì)象,來(lái)控制程序的行為。例如Foo類用到了memcache,可以在不修改Foo類代碼的情況下,改用redis。
使用依賴注入容器后的思路是應(yīng)用程序需要到Foo類,就從容器內(nèi)取得Foo類,容器創(chuàng)建Bim類,再創(chuàng)建Bar類并把Bim注入,再創(chuàng)建Foo類,并把Bar注入,應(yīng)用程序調(diào)用Foo方法,F(xiàn)oo調(diào)用Bar方法,接著做些其它工作.
總之容器負(fù)責(zé)實(shí)例化,注入依賴,處理依賴關(guān)系等工作。
代碼演示 依賴注入容器 (dependency injection container)通過(guò)一個(gè)最簡(jiǎn)單的容器類來(lái)解釋一下,這段代碼來(lái)自 Twittee
class Container { private $s = array(); function __set($k, $c) { $this->s[$k] = $c; } function __get($k) { return $this->s[$k]($this); } }
這段代碼使用了魔術(shù)方法,在給不可訪問(wèn)屬性賦值時(shí),__set() 會(huì)被調(diào)用。讀取不可訪問(wèn)屬性的值時(shí),__get() 會(huì)被調(diào)用。
$c = new Container(); $c->bim = function () { return new Bim(); }; $c->bar = function ($c) { return new Bar($c->bim); }; $c->foo = function ($c) { return new Foo($c->bar); }; // 從容器中取得Foo $foo = $c->foo; $foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething
這段代碼使用了匿名函數(shù)
再來(lái)一段簡(jiǎn)單的代碼演示一下,容器代碼來(lái)自simple di container
class IoC { protected static $registry = []; public static function bind($name, Callable $resolver) { static::$registry[$name] = $resolver; } public static function make($name) { if (isset(static::$registry[$name])) { $resolver = static::$registry[$name]; return $resolver(); } throw new Exception("Alias does not exist in the IoC registry."); } } IoC::bind("bim", function () { return new Bim(); }); IoC::bind("bar", function () { return new Bar(IoC::make("bim")); }); IoC::bind("foo", function () { return new Foo(IoC::make("bar")); }); // 從容器中取得Foo $foo = IoC::make("foo"); $foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething
這段代碼使用了后期靜態(tài)綁定
依賴注入容器 (dependency injection container) 高級(jí)功能真實(shí)的dependency injection container會(huì)提供更多的特性,如
自動(dòng)綁定(Autowiring)或 自動(dòng)解析(Automatic Resolution)
注釋解析器(Annotations)
延遲注入(Lazy injection)
下面的代碼在Twittee的基礎(chǔ)上,實(shí)現(xiàn)了Autowiring。
class Bim { public function doSomething() { echo __METHOD__, "|"; } } class Bar { private $bim; public function __construct(Bim $bim) { $this->bim = $bim; } public function doSomething() { $this->bim->doSomething(); echo __METHOD__, "|"; } } class Foo { private $bar; public function __construct(Bar $bar) { $this->bar = $bar; } public function doSomething() { $this->bar->doSomething(); echo __METHOD__; } } class Container { private $s = array(); public function __set($k, $c) { $this->s[$k] = $c; } public function __get($k) { // return $this->s[$k]($this); return $this->build($this->s[$k]); } /** * 自動(dòng)綁定(Autowiring)自動(dòng)解析(Automatic Resolution) * * @param string $className * @return object * @throws Exception */ public function build($className) { // 如果是匿名函數(shù)(Anonymous functions),也叫閉包函數(shù)(closures) if ($className instanceof Closure) { // 執(zhí)行閉包函數(shù),并將結(jié)果 return $className($this); } /** @var ReflectionClass $reflector */ $reflector = new ReflectionClass($className); // 檢查類是否可實(shí)例化, 排除抽象類abstract和對(duì)象接口interface if (!$reflector->isInstantiable()) { throw new Exception("Can"t instantiate this."); } /** @var ReflectionMethod $constructor 獲取類的構(gòu)造函數(shù) */ $constructor = $reflector->getConstructor(); // 若無(wú)構(gòu)造函數(shù),直接實(shí)例化并返回 if (is_null($constructor)) { return new $className; } // 取構(gòu)造函數(shù)參數(shù),通過(guò) ReflectionParameter 數(shù)組返回參數(shù)列表 $parameters = $constructor->getParameters(); // 遞歸解析構(gòu)造函數(shù)的參數(shù) $dependencies = $this->getDependencies($parameters); // 創(chuàng)建一個(gè)類的新實(shí)例,給出的參數(shù)將傳遞到類的構(gòu)造函數(shù)。 return $reflector->newInstanceArgs($dependencies); } /** * @param array $parameters * @return array * @throws Exception */ public function getDependencies($parameters) { $dependencies = []; /** @var ReflectionParameter $parameter */ foreach ($parameters as $parameter) { /** @var ReflectionClass $dependency */ $dependency = $parameter->getClass(); if (is_null($dependency)) { // 是變量,有默認(rèn)值則設(shè)置默認(rèn)值 $dependencies[] = $this->resolveNonClass($parameter); } else { // 是一個(gè)類,遞歸解析 $dependencies[] = $this->build($dependency->name); } } return $dependencies; } /** * @param ReflectionParameter $parameter * @return mixed * @throws Exception */ public function resolveNonClass($parameter) { // 有默認(rèn)值則返回默認(rèn)值 if ($parameter->isDefaultValueAvailable()) { return $parameter->getDefaultValue(); } throw new Exception("I have no idea what to do here."); } } // ---- $c = new Container(); $c->bar = "Bar"; $c->foo = function ($c) { return new Foo($c->bar); }; // 從容器中取得Foo $foo = $c->foo; $foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething // ---- $di = new Container(); $di->foo = "Foo"; /** @var Foo $foo */ $foo = $di->foo; var_dump($foo); /* Foo#10 (1) { private $bar => class Bar#14 (1) { private $bim => class Bim#16 (0) { } } } */ $foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething
以上代碼的原理參考PHP官方文檔:反射,PHP 5 具有完整的反射 API,添加了對(duì)類、接口、函數(shù)、方法和擴(kuò)展進(jìn)行反向工程的能力。 此外,反射 API 提供了方法來(lái)取出函數(shù)、類和方法中的文檔注釋。
若想進(jìn)一步提供一個(gè)數(shù)組訪問(wèn)接口,如$di->foo可以寫成$di["foo"],則需用到ArrayAccess(數(shù)組式訪問(wèn))接口 。
一些復(fù)雜的容器會(huì)有許多特性,下面列出一些相關(guān)的github項(xiàng)目,歡迎補(bǔ)充。
參考代碼Twittee
simple di container
Pimple
PHP-DI
Ding
推薦閱讀PHP程序員如何理解IoC/DI
PHP之道
PHP最佳實(shí)踐
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/20882.html
摘要:依賴注入容器管理應(yīng)用程序中的全局對(duì)象包括實(shí)例化處理依賴關(guān)系。為了解決這樣的問(wèn)題,我們?cè)俅位氐饺肿?cè)表創(chuàng)建組件。參考文章程序員如何理解依賴注入容器補(bǔ)充很多代碼背后,都是某種哲學(xué)思想的體現(xiàn)。 思想 思想是解決問(wèn)題的根本思想必須轉(zhuǎn)換成習(xí)慣構(gòu)建一套完整的思想體系是開發(fā)能力成熟的標(biāo)志——《簡(jiǎn)單之美》(前言) . 成功的軟件項(xiàng)目就是那些提交產(chǎn)物達(dá)到或超出客戶的預(yù)期的項(xiàng)目,而且開發(fā)過(guò)程符合時(shí)間和費(fèi)...
摘要:依賴注入并不限于構(gòu)造函數(shù)作為經(jīng)驗(yàn),注入最適合必須的依賴關(guān)系,比如示例中的情況注入最適合可選依賴關(guān)系,比如緩存一個(gè)對(duì)象實(shí)例。 本文翻譯自 Symfony 作者 Fabien Potencier 的 《Dependency Injection in general and the implementation of a Dependency Injection Container in P...
摘要:構(gòu)造器注入實(shí)現(xiàn)特定參數(shù)的構(gòu)造函數(shù),在新建對(duì)象時(shí)傳入所依賴類型的對(duì)象。 基本概念 1.依賴倒置(反轉(zhuǎn))原則(DIP):一種軟件架構(gòu)設(shè)計(jì)的原則(抽象概念,是一種思想)在面向?qū)ο缶幊填I(lǐng)域中,依賴反轉(zhuǎn)原則(Dependency inversion principle,DIP)是指一種特定的解耦(傳統(tǒng)的依賴關(guān)系創(chuàng)建在高層次上,而具體的策略設(shè)置則應(yīng)用在低層次的模塊上)形式,使得高層次的模塊不依賴于...
摘要:上文書,創(chuàng)建對(duì)象需要先創(chuàng)建對(duì)象。創(chuàng)建對(duì)象的雜活是嵌入在中的。對(duì)象使用來(lái)管理依賴關(guān)系非常好,但不是必須的。很容易實(shí)現(xiàn),但手工維護(hù)各種亂七八糟的對(duì)象還是很麻煩。所有文章均已收錄至項(xiàng)目。 本文翻譯自 Symfony 作者 Fabien Potencier 的 《Dependency Injection in general and the implementation of a Depend...
摘要:劃下重點(diǎn),服務(wù)容器是用于管理類的依賴和執(zhí)行依賴注入的工具。類的實(shí)例化及其依賴的注入,完全由服務(wù)容器自動(dòng)的去完成。 本文首發(fā)于 深入剖析 Laravel 服務(wù)容器,轉(zhuǎn)載請(qǐng)注明出處。喜歡的朋友不要吝嗇你們的贊同,謝謝。 之前在 深度挖掘 Laravel 生命周期 一文中,我們有去探究 Laravel 究竟是如何接收 HTTP 請(qǐng)求,又是如何生成響應(yīng)并最終呈現(xiàn)給用戶的工作原理。 本章將帶領(lǐng)大...
閱讀 3570·2021-11-12 10:36
閱讀 2969·2021-09-22 15:35
閱讀 2887·2021-09-04 16:41
閱讀 1248·2019-08-30 15:55
閱讀 3651·2019-08-29 18:43
閱讀 2138·2019-08-23 18:24
閱讀 1481·2019-08-23 18:10
閱讀 1979·2019-08-23 11:31