国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發 > PHP > 正文

php中依賴注入深入理解

2024-05-04 21:50:20
字體:
來源:轉載
供稿:網友

PHP程序員如何理解依賴注入容器(dependency injection container)

背景知識

傳統的思路是應用程序用到一個Foo類,就會創建Foo類并調用Foo類的方法,假如這個方法內需要一個Bar類,就會創建Bar類并調用Bar類的方法,而這個方法內需要一個Bim類,就會創建Bim類,接著做些其它工作。

  1. <?php 
  2. // 代碼【1】 
  3. class Bim 
  4.     public function doSomething() 
  5.     { 
  6.         echo __METHOD__'|'
  7.     } 
  8.  
  9. class Bar 
  10.     public function doSomething() 
  11.     { 
  12.         $bim = new Bim(); 
  13.         $bim->doSomething(); 
  14.         echo __METHOD__'|'
  15.     } 
  16.  
  17. class Foo 
  18.     public function doSomething() 
  19.     { 
  20.         $bar = new Bar(); 
  21.         $bar->doSomething(); 
  22.         echo __METHOD__
  23.     } 
  24.  
  25. $foo = new Foo(); 
  26. $foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething 

使用依賴注入的思路是應用程序用到Foo類,Foo類需要Bar類,Bar類需要Bim類,那么先創建Bim類,再創建Bar類并把Bim注入,再創建Foo類,并把Bar類注入,再調用Foo方法,Foo調用Bar方法,接著做些其它工作。

  1. <?php 
  2. // 代碼【2】 
  3. class Bim 
  4.     public function doSomething() 
  5.     { 
  6.         echo __METHOD__'|'
  7.     } 
  8.  
  9. class Bar 
  10.     private $bim
  11.  
  12.     public function __construct(Bim $bim
  13.     { 
  14.         $this->bim = $bim
  15.     } 
  16.  
  17.     public function doSomething() 
  18.     { 
  19.         $this->bim->doSomething(); 
  20.         echo __METHOD__'|'
  21.     } 
  22.  
  23. class Foo 
  24.     private $bar
  25.  
  26.     public function __construct(Bar $bar
  27.     { 
  28.         $this->bar = $bar
  29.     } 
  30.  
  31.     public function doSomething() 
  32.     { 
  33.         $this->bar->doSomething(); 
  34.         echo __METHOD__
  35.     } 
  36.  
  37. $foo = new Foo(new Bar(new Bim())); 
  38. $foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething 

這就是控制反轉模式。依賴關系的控制反轉到調用鏈的起點。這樣你可以完全控制依賴關系,通過調整不同的注入對象,來控制程序的行為。例如Foo類用到了memcache,可以在不修改Foo類代碼的情況下,改用redis。

使用依賴注入容器后的思路是應用程序需要到Foo類,就從容器內取得Foo類,容器創建Bim類,再創建Bar類并把Bim注入,再創建Foo類,并把Bar注入,應用程序調用Foo方法,Foo調用Bar方法,接著做些其它工作.

總之容器負責實例化,注入依賴,處理依賴關系等工作。

代碼演示 依賴注入容器 (dependency injection container)

通過一個最簡單的容器類來解釋一下,這段代碼來自 Twittee

  1. <?php 
  2.  
  3. class Container 
  4.     private $s = array(); 
  5.  
  6.     function __set($k$c
  7.     { 
  8.         $this->s[$k] = $c
  9.     } 
  10.  
  11.     function __get($k
  12.     { 
  13.         return $this->s[$k]($this); 
  14.     } 

這段代碼使用了魔術方法,在給不可訪問屬性賦值時,__set() 會被調用。讀取不可訪問屬性的值時,__get() 會被調用。

  1. <?php 
  2.  
  3. $c = new Container(); 
  4.  
  5. $c->bim = function () { 
  6.     return new Bim(); 
  7. }; 
  8. $c->bar = function ($c) { 
  9.     return new Bar($c->bim); 
  10. }; 
  11. $c->foo = function ($c) { 
  12.     return new Foo($c->bar); 
  13. }; //Vevb.com 
  14.  
  15. // 從容器中取得Foo 
  16. $foo = $c->foo; 
  17. $foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething 

這段代碼使用了匿名函數

再來一段簡單的代碼演示一下,容器代碼來自simple di container

  1. <?php 
  2.  
  3. class IoC 
  4.     protected static $registry = []; 
  5.  
  6.     public static function bind($name, Callable $resolver
  7.     { 
  8.         static::$registry[$name] = $resolver
  9.     } 
  10.  
  11.     public static function make($name
  12.     { 
  13.         if (isset(static::$registry[$name])) { 
  14.             $resolver = static::$registry[$name]; 
  15.             return $resolver(); 
  16.         } 
  17.         throw new Exception('Alias does not exist in the IoC registry.'); 
  18.     } 
  19.  
  20. IoC::bind('bim'function () { 
  21.     return new Bim(); 
  22. }); 
  23. IoC::bind('bar'function () { 
  24.     return new Bar(IoC::make('bim')); 
  25. }); 
  26. IoC::bind('foo'function () { 
  27.     return new Foo(IoC::make('bar')); 
  28. }); 
  29.  
  30.  
  31. // 從容器中取得Foo 
  32. $foo = IoC::make('foo'); 
  33. $foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething 

這段代碼使用了后期靜態綁定

依賴注入容器 (dependency injection container) 高級功能

真實的dependency injection container會提供更多的特性,如:

自動綁定(Autowiring)或 自動解析(Automatic Resolution)

注釋解析器(Annotations)

延遲注入(Lazy injection)

下面的代碼在Twittee的基礎上,實現了Autowiring。

  1. <?php 
  2.  
  3. class Bim 
  4.     public function doSomething() 
  5.     { 
  6.         echo __METHOD__'|'
  7.     } 
  8.  
  9. class Bar 
  10.     private $bim
  11.  
  12.     public function __construct(Bim $bim
  13.     { 
  14.         $this->bim = $bim
  15.     } 
  16.  
  17.     public function doSomething() 
  18.     { 
  19.         $this->bim->doSomething(); 
  20.         echo __METHOD__'|'
  21.     } 
  22.  
  23. class Foo 
  24.     private $bar
  25.  
  26.     public function __construct(Bar $bar
  27.     { 
  28.         $this->bar = $bar
  29.     } 
  30.  
  31.     public function doSomething() 
  32.     { 
  33.         $this->bar->doSomething(); 
  34.         echo __METHOD__
  35.     } 
  36.  
  37. class Container 
  38.     private $s = array(); 
  39.  
  40.     public function __set($k$c
  41.     { 
  42.         $this->s[$k] = $c
  43.     } 
  44.  
  45.     public function __get($k
  46.     { 
  47.         // return $this->s[$k]($this); 
  48.         return $this->build($this->s[$k]); 
  49.     } 
  50.  
  51.     /** 
  52.      * 自動綁定(Autowiring)自動解析(Automatic Resolution) 
  53.      * 
  54.      * @param string $className 
  55.      * @return object 
  56.      * @throws Exception 
  57.      */ 
  58.     public function build($className
  59.     { 
  60.         // 如果是匿名函數(Anonymous functions),也叫閉包函數(closures) 
  61.         if ($className instanceof Closure) { 
  62.             // 執行閉包函數,并將結果 
  63.             return $className($this); 
  64.         } 
  65.  
  66.         /** @var ReflectionClass $reflector */ 
  67.         $reflector = new ReflectionClass($className); 
  68.  
  69.         // 檢查類是否可實例化, 排除抽象類abstract和對象接口interface 
  70.         if (!$reflector->isInstantiable()) { 
  71.             throw new Exception("Can't instantiate this."); 
  72.         } 
  73.  
  74.         /** @var ReflectionMethod $constructor 獲取類的構造函數 */ 
  75.         $constructor = $reflector->getConstructor(); 
  76.  
  77.         // 若無構造函數,直接實例化并返回 
  78.         if (is_null($constructor)) { 
  79.             return new $className
  80.         } 
  81.  
  82.         // 取構造函數參數,通過 ReflectionParameter 數組返回參數列表 
  83.         $parameters = $constructor->getParameters(); 
  84.  
  85.         // 遞歸解析構造函數的參數 
  86.         $dependencies = $this->getDependencies($parameters); 
  87.  
  88.         // 創建一個類的新實例,給出的參數將傳遞到類的構造函數。 
  89.         return $reflector->newInstanceArgs($dependencies); 
  90.     } 
  91.  
  92.     /** 
  93.      * @param array $parameters 
  94.      * @return array 
  95.      * @throws Exception 
  96.      */ 
  97.     public function getDependencies($parameters
  98.     { 
  99.         $dependencies = []; 
  100.  
  101.         /** @var ReflectionParameter $parameter */ 
  102.         foreach ($parameters as $parameter) { 
  103.             /** @var ReflectionClass $dependency */ 
  104.             $dependency = $parameter->getClass(); 
  105.  
  106.             if (is_null($dependency)) { 
  107.                 // 是變量,有默認值則設置默認值 
  108.                 $dependencies[] = $this->resolveNonClass($parameter); 
  109.             } else { 
  110.                 // 是一個類,遞歸解析 
  111.                 $dependencies[] = $this->build($dependency->name); 
  112.             } 
  113.         } 
  114.  
  115.         return $dependencies
  116.     } 
  117.  
  118.     /** 
  119.      * @param ReflectionParameter $parameter 
  120.      * @return mixed 
  121.      * @throws Exception 
  122.      */ 
  123.     public function resolveNonClass($parameter
  124.     { 
  125.         // 有默認值則返回默認值 
  126.         if ($parameter->isDefaultValueAvailable()) { 
  127.             return $parameter->getDefaultValue(); 
  128.         } 
  129.  
  130.         throw new Exception('I have no idea what to do here.'); 
  131.     } 
  132.  
  133. // ---- 
  134. $c = new Container(); 
  135. $c->bar = 'Bar'
  136. $c->foo = function ($c) { 
  137.     return new Foo($c->bar); 
  138. }; 
  139. // 從容器中取得Foo 
  140. $foo = $c->foo; 
  141. $foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething 
  142.  
  143. // ---- 
  144. $di = new Container(); 
  145.  
  146. $di->foo = 'Foo'
  147.  
  148. /** @var Foo $foo */ 
  149. $foo = $di->foo; 
  150.  
  151. var_dump($foo); 
  152. /* 
  153. Foo#10 (1) { 
  154.   private $bar => 
  155.   class Bar#14 (1) { 
  156.     private $bim => 
  157.     class Bim#16 (0) { 
  158.     } 
  159.   } 
  160. } 
  161. */ 
  162.  
  163. $foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething 

以上代碼的原理參考PHP官方文檔:反射,PHP 5 具有完整的反射 API,添加了對類、接口、函數、方法和擴展進行反向工程的能力。 此外,反射 API 提供了方法來取出函數、類和方法中的文檔注釋。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 滦南县| 庆城县| 仁化县| 象州县| 柏乡县| 白玉县| 剑川县| 锡林浩特市| 安康市| 阜康市| 洛隆县| 塔城市| 乐陵市| 明溪县| 奉节县| 兰西县| 平利县| 蛟河市| 梁河县| 拉萨市| 德保县| 新安县| 防城港市| 揭阳市| 彭阳县| 河北省| 随州市| 金乡县| 永胜县| 拉孜县| 贡觉县| 牟定县| 河津市| 紫云| 新竹县| 宁晋县| 青海省| 沁源县| 维西| 尚义县| 祁连县|