User: Jeremy.Ke
Time: 2019/6/25 3:32
依赖注入和控制反转是对同一件事情的不同描述,从某个方面讲,就是它们描述的角度不同。
(1)依赖注入是从应用程序的角度在描述,可以把依赖注入,即:应用程序依赖容器创建并注入它所需要的外部资源;
(2)而控制反转是从容器的角度在描述,即:容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源.
使用依赖注入,最重要的一点好处就是有效的分离了对象和它所需要的外部资源,使得它们松散耦合,有利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。
(1)整个过程中的参与者?
一般有三方参与者,一个是某个对象;一个是IoC/DI的容器;另一个是某个对象的外部资源。
其一:某个对象指的就是任意的、普通的PHP对象;
其二:IoC/DI的容器简单点说就是指用来实现IoC/DI功能的一个框架程序;
其三:对象的外部资源指的就是对象需要的,但是是从对象外部获取的,都统称资源,比如:对象需要的其它对象、或者是对象需要的文件资源等等。
(2)谁依赖于谁?为什么需要依赖?
某个对象依赖于IoC/DI的容器,对象需要IoC/DI的容器来提供对象需要的外部资源.
(3)谁注入于谁?注入什么?
IoC/DI的容器 注入(某些资源到)某个对象,注入的对象所需的资源。
(4)控制什么?为何叫反转?什么是正转?
主要是控制对象实例的创建。
A类的实例化需要资源B,直接在A类中去获取配置文件或者实例化B获取资源B,叫做正转;
A类不再主动去获取C,而是被动等待,等待IoC/DI的容器获取一个C的实例,然后反向的注入到A类中,叫做反转。
图解:
其实IoC/DI对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,
要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC/DI容器来创建并注入它所需要的资源了。
class B
{
public function bsay()
{
echo "b say";
}
}
class A
{
public function asay()
{
$b_obj = new B;
$b_obj->bsay();
}
}
$a_obj = new A;
$a_obj->asay();
class B
{
public function bsay()
{
echo "b say";
}
}
class A
{
private $_bobj;
public function __construct($b_obj)
{
$this->_bobj = $b_obj;
}
public function asay()
{
$this->_bobj->bsay();
}
}
$b_obj = new B;
$a_obj = new A($b_obj);
$a_obj->asay();
?>
但是如果A需要的资源不只是B的对象,还有C,D…这个时候,你就需要在构造函数写很多变量来接收这个资源,显然,这是不合理的。这个
时候需要一个专门的容器来存储所要需要的资源。
class DI
{
public $b;
public $c;
public $d;
public function __construct()
{
$this->c = new C();
$this->d = new D();
$this->b = new B();
}
}
class B
{
public function bsay()
{
echo "b say";
}
}
class C
{
public function csay()
{
echo "c say";
}
}
class D
{
public function dsay()
{
echo "d say";
}
}
class A
{
private $_bobj;
public function __construct($b_obj)
{
$this->_bobj = $b_obj;
}
public function asay()
{
$this->_bobj->bsay();
}
}
$di_obj = new DI();
$a_obj = new A($di_obj->b);
$a_obj->asay();
?>
以上基本能满足如何使用依赖注入解决我们的问题。不是在代码内部创建依赖关系,而是让其作为一个参数传递,这使得我们的程序更容易维
护,降低程序代码的耦合度,实现一种松耦合。但是从长远来看,这种形式的依赖注入也有一些缺点。例如,如果组件中有较多的依赖关系,
我们需要创建多个setter方法传递,或创建构造函数进行传递。另外,每次使用组件时,都需要创建依赖组件,使代码维护不太易。我们需要修改
一下DI这个容器类。
class Di
{
private $container = array();
public function set($key, $obj,$arg)
{
if(count($arg) == 1 && is_array($arg[0])){
$arg = $arg[0];
}
/*
* 注入的时候不做任何的类型检测与转换
* 由于编程人员为问题,该注入资源并不一定会被用到
*/
$this->container[$key] = array(
"obj"=>$obj,
"params"=>$arg,
);
}
function delete($key)
{
unset( $this->container[$key]);
}
function clear()
{
$this->container = array();
}
function get($key)
{
if(isset($this->container[$key])){
$result = $this->container[$key];
if(is_object($result['obj'])){
return $result['obj'];
}else if(is_callable($result['obj'])){
return $this->container[$key]['obj'];
}else if(is_string($result['obj']) && class_exists($result['obj'])){
$reflection = new \ReflectionClass ( $result['obj'] );
$ins = $reflection->newInstanceArgs ( $result['params'] );
$this->container[$key]['obj'] = $ins;
return $this->container[$key]['obj'];
}else{
return $result['obj'];
}
}else{
return null;
}
}
}
class B
{
public function bsay()
{
echo "b say";
}
}
class A
{
private $_bobj;
public function __construct($b_obj)
{
$this->_bobj = $b_obj;
}
public function asay()
{
$this->_bobj->bsay();
}
}
$di_obj = new DI();
$di_obj->set('b_obj','B');
$a_obj = new A($di_obj->get('b_obj'));
$a_obj->asay();
?>
现在,该Di类只有访问某种service的时候才需要它,如果它不需要,它甚至不初始化,以节约资源。该组件是高度解耦。他们的行为,或者说他们的任
何其他方面都不会影响到组件本身。
优点:
(1)我们可以更换一个依赖资源,从他们本身或者第三方轻松创建。
(2)我们可以充分的控制对象的初始化,并对对象进行各种设置。
(3)我们可以使用统一的方式从容器中,得到一个结构化的全局实例。
(4)DI类。除非开发人员在注入服务的时候直接实例化一个对象,然后存存储到容器中。在容器中,通过数组,字符串等方式存储的服务都将被延迟加载,即只有在请求对象的时候才被初始化。