以前一直对依赖注入和控制反转理解不清,或理解不深,这里总结一番!
1. 什么是依赖注入?
IOC:英文全称:Inversion of Control,简称IOC,中文名称:控制反转,它还有个名字叫依赖注入(Dependency Injection,简称DI)。
当一个类的实例需要另一个类的实例协助时,在传统的程序设计过程中,通常由调用者来创建被调用者的实例。而采用依赖注入的方式,创建被调用者的工作不再由调用者来完成,因此叫控制反转,创建被调用者的实例的工作由IOC容器来完成,然后注入调用者,因此也称为依赖注入。
下面通过例子解析:
class A
{
public $b;
public $c;
public function A()
{
//TODO
}
public function Method()
{
$this->b=new B();
$this->c=new C();
$this->b->Method();
$this->c->Method();
//TODO
}
}
class B
{
public function B()
{
//TODO
}
public function Method()
{
//TODO
echo 'b';
}
}
class C
{
public function C()
{
//TODO
}
public function Method()
{
//TODO
echo 'c';
}
}
$a=new A();
$a->Method();
?>
上面代码,我们很容易理解一句话:A类依赖B类和C类
也就是说,如果今后开发过程中,要对B类或者C类修改,一旦涉及函数改名,函数参数数量变动,甚至整个类结构的调整,我们也要对A类做出相应的调整,A类的独立性丧失了,这在开发过程中是很不方便的,也就是我们说的“牵一发动全身”,如果两个类是两个人分别写的,矛盾往往就在这个时候产生了。。。
万一真的要改动B类和C类,有没有办法,可以不去改动或者尽量少改动A类的代码呢?这里要用到控制反转。
控制反转(IOC)是一种思想,依赖注入(DI)是实施这种思想的方法。
第一种方法叫做:构造器注入(这种方法也不推荐用,但比不用要好)
class A
{
public $b;
public $c;
public function A($b,$c)
{
$this->b=$b;
$this->c=$c;
}
public function Method()
{
$this->b->Method();
$this->c->Method();
}
}
$a=new A(new B(),new C());
$a->Method();
A类的构造器依赖B类和C类,通过构造器的参数传入,至少实现了一点,就是B类对象b和C类对象c的创建都移至了A类外,所以一旦B类和C类发生改动,A类无需做修改,只要在client类里改就可以了
第二种方法叫做:工厂模式注入(可以使用)
新添加一个工厂
class Factory
{
public function Factory()
{
//TODO
}
public function create($s)
{
switch($s)
{
case 'B':
{
return new B();
break;
}
case 'C':
{
return new C();
break;
}
default:
{
return null;
break;
}
}
}
}
改动A类为:
class A
{
public $b;
public $c;
public function A()
{
//TODO
}
public function Method()
{
$f=new Factory();
$this->b=$f->create('B');
$this->c=$f->create('C');
$this->b->Method();
$this->c->Method();
//TODO
}
}
这是一个使用工厂模块的依赖注入和控制反转具体实例。A类不再直接生成B类,C类,而交给工厂控制生成(这是一个控制反转),生成实例后注入到A类的变量中,这里实现了依赖注入。
使用工厂模式虽然很多程度解耦了代码,但是除了B,C还有更多D,E,F等等的时候呢,这时候,我们可以使用registry注册。
class Ioc {
/**
* @var 注册的依赖数组
*/
protected static $registry = array();
/**
* 添加一个resolve到registry数组中
* @param string $name 依赖标识
* @param object $resolve 一个匿名函数用来创建实例
* @return void
*/
public static function register($name, Closure $resolve)
{
static::$registry[$name] = $resolve;
}
/**
* 返回一个实例
* @param string $name 依赖的标识
* @return mixed
*/
public static function resolve($name)
{
if ( static::registered($name) )
{
$name = static::$registry[$name];
return $name();
}
throw new Exception('Nothing registered with that name, fool.');
}
/**
* 查询某个依赖实例是否存在
* @param string $name id
* @return bool
*/
public static function registered($name)
{
return array_key_exists($name, static::$registry);
}
}
现在就可以通过如下方式来注册和注入一个
$book = Ioc::registry('book', function(){
$book = new Book;
$book->setdb('...');
$book->setprice('...');
return $book;
});
//注入依赖
$book = Ioc::resolve('book');
?>
这样我们需要用到那个类,就直接使用DI注册一个
这里相当于抽象了一个万能工厂,这个工厂什么都可以做,但是没有材料,需要你提供加工材料 ,他只负责加工,并且保存,下次需要直接过来拿,不需要重新加工;而上面的工厂模式,这个工厂已经提供了材料,但是提供的材料有限,他只能在这些已经存在的材料中加工,区别就在这里。
参考文章有: