依赖注入模式
1. https://my.oschina.net/cxz001/blog/533166
依赖注入(Dependency Injection)是控制反转(Inversion of Control)的一种实现方式。 我们先来看看什么是控制反转。 当调用者需要被调用者的协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例,但在这里,创建被调用者的工作不再由调用者来完成,
而是将被调用者的创建移到调用者的外部,从而反转被调用者的创建,消除了调用者对被调用者创建的控制,因此称为控制反转。
要实现控制反转,通常的解决方案是将创建被调用者实例的工作交由 IoC 容器来完成,然后在调用者中注入被调用者(通过构造器/方法注入实现),这样我们就实现了调用者与被调用者的解耦,该过程被称为依赖注入。
依赖注入不是目的,它是一系列工具和手段,最终的目的是帮助我们开发出松散耦合(loose coupled)、可维护、可测试的代码和程序。这条原则的做法是大家熟知的面向接口,或者说是面向抽象编程。
下面是一个简化版本的Ioc容器, 完整版的可以观看相应框架的实现
1 <?php 2 3 /** 4 * Ioc容器,这个是简化版本的,有些特性还是没有包含的 5 */ 6 class Di implements \ArrayAccess{ 7 private $_bindings = []; //服务列表 8 private $_instances = []; //已经实例化的服务 9 10 //获取服务 11 public function get($name, $params=[]){ 12 //先从已经实例化的列表中查找 13 if(isset($this->_instances[$name])){ 14 return $this->_instances[$name]; 15 } 16 17 //检测有没有注册该服务 18 if(!isset($this->_bindings[$name])){ 19 return null; 20 } 21 22 $concrete = $this->_bindings[$name]['class'];//对象具体注册内容 23 24 $obj = null; 25 //匿名函数方式 26 if($concrete instanceof \Closure){ 27 $obj = call_user_func_array($concrete,$params); 28 } 29 //字符串方式 30 elseif(is_string($concrete)){ 31 if(empty($params)){ 32 $obj = new $concrete; 33 }else{ 34 //带参数的类实例化,使用反射 35 $class = new \ReflectionClass($concrete); 36 $obj = $class->newInstanceArgs($params); 37 } 38 } 39 40 //如果是共享服务,则写入_instances列表,下次直接取回 41 if($this->_bindings[$name]['shared'] == true && $obj){ 42 $this->_instances[$name] = $obj; 43 } 44 45 return $obj; 46 } 47 48 //检测是否已经绑定 49 public function has($name){ 50 return isset($this->_bindings[$name]) or isset($this->_instances[$name]); 51 } 52 53 //卸载服务 54 public function remove($name){ 55 unset($this->_bindings[$name], $this->_instances[$name]); 56 } 57 58 //设置服务 59 public function set($name,$class){ 60 $this->_registerService($name, $class); 61 } 62 63 //设置共享服务 64 public function setShared($name,$class){ 65 $this->_registerService($name, $class, true); 66 } 67 68 69 70 71 72 //注册服务 73 private function _registerService($name,$class,$shared=false){ 74 $this->remove($name); 75 76 if(!($class instanceof \Closure) && is_object($class)){ 77 $this->_instances[$name] = $class; 78 }else{ 79 $this->_bindings[$name] = array("class"=>$class,"shared"=>$shared); 80 } 81 } 82 83 //ArrayAccess接口,检测服务是否存在 84 public function offsetExists($offset) { 85 return $this->has($offset); 86 } 87 88 //ArrayAccess接口,以$di[$name]方式获取服务 89 public function offsetGet($offset) { 90 return $this->get($offset); 91 } 92 93 //ArrayAccess接口,以$di[$name]=$value方式注册服务,非共享 94 public function offsetSet($offset, $value) { 95 return $this->set($offset,$value); 96 } 97 98 //ArrayAccess接口,以unset($di[$name])方式卸载服务 99 public function offsetUnset($offset) { 100 return $this->remove($offset); 101 } 102 } 103 104 105 106 107 108 header("Content-Type:text/html;charset=utf8"); 109 class A{ 110 public $name; 111 112 public function __construct($name=""){ 113 $this->name = $name; 114 } 115 } 116 117 $di = new Di(); 118 119 120 //匿名函数方式注册一个名为a1的服务 121 $di->setShared('a1',function($name=""){ 122 return new A($name); 123 }); 124 125 $a1 = $di->get('a1',array("小李")); 126 echo $a1->name."<br/>";//小李 127 128 $a1_1 = $di->get('a1',array("小王")); 129 echo $a1->name."<br/>";//小李 130 echo $a1_1->name."<br/>";//小李 131 132 133 134 //直接以类名方式注册 135 $di->set('a2','A'); 136 137 $a2 = $di->get('a2',array("小张")); 138 echo $a2->name."<br/>";//小张 139 $a2_1 = $di->get('a2',array("小徐")); 140 echo $a2->name."<br/>";//小张 141 echo $a2_1->name."<br/>";//小徐 142 143 144 145 //直接传入实例化的对象 146 $di->set('a3',new A("小唐")); 147 148 $a3 = $di['a3'];//可以直接通过数组方式获取服务对象 149 echo $a3->name."<br/>";//小唐 150 151 152 153 154 155 156 157 //删除服务则可以通过 158 unset($di['a1']); 159 160 or 161 162 $di->remove('a1'); 163 164 165 //判断是否包含一个服务可以通过 166 isset($di['a1']); 167 168 or 169 170 $di->has('a1');