设计思想依赖注入
Posted 半生归来
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计思想依赖注入相关的知识,希望对你有一定的参考价值。
场景:传统的思路是应用程序用到一个Foo类,就会创建Foo类并调用Foo类的方法,假如这个方法内需要一个Bar类,就会创建Bar类并调用Bar类的方法,而这个方法内需要一个Bim类,就会创建Bim类,接着做些其它工作。
【代码1】
1 class Bim 2 { 3 public function doSomething() 4 { 5 echo __METHOD__, ‘|‘; 6 } 7 } 8 9 class Bar 10 { 11 public function doSomething() 12 { 13 $bim = new Bim(); 14 $bim->doSomething(); 15 echo __METHOD__, ‘|‘; 16 } 17 } 18 19 class Foo 20 { 21 public function doSomething() 22 { 23 $bar = new Bar(); 24 $bar->doSomething(); 25 echo __METHOD__; 26 } 27 } 28 29 $foo = new Foo(); 30 $foo->doSomething(); //Bim::doSomething|Bar::doSomething|Foo::doSomething
使用依赖注入的思路是应用程序用到Foo类,Foo类需要Bar类,Bar类需要Bim类,那么先创建Bim类,再创建Bar类并把Bim注入,再创建Foo类,并把Bar类注入,再调用Foo方法,Foo调用Bar方法,接着做些其它工作。
1 class Bim 2 { 3 public function doSomething() 4 { 5 echo __METHOD__, ‘|‘; 6 } 7 } 8 9 class Bar 10 { 11 private $bim; 12 13 public function __construct(Bim $bim) 14 { 15 $this->bim = $bim; 16 } 17 18 public function doSomething() 19 { 20 $this->bim->doSomething(); 21 echo __METHOD__, ‘|‘; 22 } 23 } 24 25 class Foo 26 { 27 private $bar; 28 29 public function __construct(Bar $bar) 30 { 31 $this->bar = $bar; 32 } 33 34 public function doSomething() 35 { 36 $this->bar->doSomething(); 37 echo __METHOD__; 38 } 39 } 40 41 $foo = new Foo(new Bar(new Bim())); 42 $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)
通过一个最简单的容器类来解释一下
1 class Container 2 { 3 private $s = array(); 4 5 function __set($k, $c) 6 { 7 $this->s[$k] = $c; 8 } 9 10 function __get($k) 11 { 12 return $this->s[$k]($this); 13 } 14 }
这段代码使用了魔术方法,在给不可访问属性赋值时,__set() 会被调用。读取不可访问属性的值时,__get() 会被调用。
1 $c = new Container(); 2 3 $c->bim = function () { 4 return new Bim(); 5 }; 6 $c->bar = function ($c) { 7 return new Bar($c->bim); 8 }; 9 $c->foo = function ($c) { 10 return new Foo($c->bar); 11 }; 12 13 // 从容器中取得Foo 14 $foo = $c->foo; 15 $foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething
这段代码使用了匿名函数
再来一段简单的代码演示一下,容器代码来自simple di container
1 class IoC 2 { 3 protected static $registry = []; 4 5 public static function bind($name, Callable $resolver) 6 { 7 static::$registry[$name] = $resolver; 8 } 9 10 public static function make($name) 11 { 12 if (isset(static::$registry[$name])) { 13 $resolver = static::$registry[$name]; 14 return $resolver(); 15 } 16 throw new Exception(‘Alias does not exist in the IoC registry.‘); 17 } 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) 高级功能
1 class Bim 2 { 3 public function doSomething() 4 { 5 echo __METHOD__, ‘|‘; 6 } 7 } 8 9 class Bar 10 { 11 private $bim; 12 13 public function __construct(Bim $bim) 14 { 15 $this->bim = $bim; 16 } 17 18 public function doSomething() 19 { 20 $this->bim->doSomething(); 21 echo __METHOD__, ‘|‘; 22 } 23 } 24 25 class Foo 26 { 27 private $bar; 28 29 public function __construct(Bar $bar) 30 { 31 $this->bar = $bar; 32 } 33 34 public function doSomething() 35 { 36 $this->bar->doSomething(); 37 echo __METHOD__; 38 } 39 } 40 41 class Container 42 { 43 private $s = array(); 44 45 public function __set($k, $c) 46 { 47 $this->s[$k] = $c; 48 } 49 50 public function __get($k) 51 { 52 // return $this->s[$k]($this); 53 return $this->build($this->s[$k]); 54 } 55 56 /** 57 * 自动绑定(Autowiring)自动解析(Automatic Resolution) 58 * 59 * @param string $className 60 * @return object 61 * @throws Exception 62 */ 63 public function build($className) 64 { 65 // 如果是匿名函数(Anonymous functions),也叫闭包函数(closures) 66 if ($className instanceof Closure) { 67 // 执行闭包函数,并将结果 68 return $className($this); 69 } 70 71 /** @var ReflectionClass $reflector */ 72 $reflector = new ReflectionClass($className); 73 74 // 检查类是否可实例化, 排除抽象类abstract和对象接口interface 75 if (!$reflector->isInstantiable()) { 76 throw new Exception("Can‘t instantiate this."); 77 } 78 79 /** @var ReflectionMethod $constructor 获取类的构造函数 */ 80 $constructor = $reflector->getConstructor(); 81 82 // 若无构造函数,直接实例化并返回 83 if (is_null($constructor)) { 84 return new $className; 85 } 86 87 // 取构造函数参数,通过 ReflectionParameter 数组返回参数列表 88 $parameters = $constructor->getParameters(); 89 90 // 递归解析构造函数的参数 91 $dependencies = $this->getDependencies($parameters); 92 93 // 创建一个类的新实例,给出的参数将传递到类的构造函数。 94 return $reflector->newInstanceArgs($dependencies); 95 } 96 97 /** 98 * @param array $parameters 99 * @return array 100 * @throws Exception 101 */ 102 public function getDependencies($parameters) 103 { 104 $dependencies = []; 105 106 /** @var ReflectionParameter $parameter */ 107 foreach ($parameters as $parameter) { 108 /** @var ReflectionClass $dependency */ 109 $dependency = $parameter->getClass(); 110 111 if (is_null($dependency)) { 112 // 是变量,有默认值则设置默认值 113 $dependencies[] = $this->resolveNonClass($parameter); 114 } else { 115 // 是一个类,递归解析 116 $dependencies[] = $this->build($dependency->name); 117 } 118 } 119 120 return $dependencies; 121 } 122 123 /** 124 * @param ReflectionParameter $parameter 125 * @return mixed 126 * @throws Exception 127 */ 128 public function resolveNonClass($parameter) 129 { 130 // 有默认值则返回默认值 131 if ($parameter->isDefaultValueAvailable()) { 132 return $parameter->getDefaultValue(); 133 } 134 135 throw new Exception(‘I have no idea what to do here.‘); 136 } 137 } 138 139 // ---- 140 $c = new Container(); 141 $c->bar = ‘Bar‘; 142 $c->foo = function ($c) { 143 return new Foo($c->bar); 144 }; 145 // 从容器中取得Foo 146 $foo = $c->foo; 147 $foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething 148 149 // ---- 150 $di = new Container(); 151 152 $di->foo = ‘Foo‘; 153 154 /** @var Foo $foo */ 155 $foo = $di->foo; 156 157 var_dump($foo); 158 /* 159 Foo#10 (1) { 160 private $bar => 161 class Bar#14 (1) { 162 private $bim => 163 class Bim#16 (0) { 164 } 165 } 166 } 167 */ 168 169 $foo->doSomething(); // Bim::doSomething|Bar::doSomething|Foo::doSomething
以上代码的原理参考php官方文档:反射,PHP 5 具有完整的反射 API,添加了对类、接口、函数、方法和扩展进行反向工程的能力。 此外,反射 API 提供了方法来取出函数、类和方法中的文档注释。
若想进一步提供一个数组访问接口,如$di->foo可以写成$di‘foo‘],则需用到[ArrayAccess(数组式访问)接口 。
一些复杂的容器会有许多特性
以上是关于设计思想依赖注入的主要内容,如果未能解决你的问题,请参考以下文章
spring练习,在Eclipse搭建的Spring开发环境中,使用set注入方式,实现对象的依赖关系,通过ClassPathXmlApplicationContext实体类获取Bean对象(代码片段