依赖倒置原则 DIP(Dependence Inversion Principle)

依赖倒置原则 DIP(Dependence Inversion Principle)html

控制反转(Inversion of Control, IoC) IoC就是DIP的一种具体思路,DIP只是一种理念、思想,而IoC是一种实现DIP的方法。 IoC的核心是将类(上层)所依赖的单元(下层)的实例化过程交由第三方来实现。 一个简单的特征,就是类中不对所依赖的单元有诸如 $component = new yii\component\SomeClass() 的实例化语句。 . 依赖注入(Dependence Injection, DI) DI是IoC的一种设计模式,是一种套路,按照DI的套路,就能够实现IoC,就能符合DIP原则。 DI的核心是把类所依赖的单元的实例化过程,放到类的外面去实现。设计模式

控制反转容器(IoC Container) 当项目比较大时,依赖关系可能会很复杂。 而IoC Container提供了动态地建立、注入依赖单元,映射依赖关系等功能,减小了许多代码量。yii

服务定位器(Service Locator) Service Locator是IoC的另外一种实现方式, 其核心是把全部可能用到的依赖单元交由Service Locator进行实例化和建立、配置, 把类对依赖单元的依赖,转换成类对Service Locator的依赖。 DI 与 Service Locator并不冲突,二者能够结合使用。函数

依赖注入测试

场景:假设要实现当访客在博客上发表评论后,向博文的做者发送Email的功能,一般代码会是这样:

//为邮件服务定义抽象层
interface IEmailSender{
	public function send(...$param);
	...
}

//定义Gmail邮件服务
class GmailSender implements IEmailSender{
	public function send(...$param){
		...
	}
	...
}

//定义评论类
class Comment extends CommentDB{
	//用于引用发送邮件的库
	private $_eMailSender;

	//初始化时,实例化$eMailSender
	public function init(){
		//这里假设使用Gmail邮件服务
		$this->_eMailSender = new GmailSender();
	}

	//当有新评论,即save()方法被调用以后中,会触发该方法
	public function afterInsert(){
		$this->_eMailSender->send(...$param);
		...
	}
}

试想若是如今咱们不想使用Gmail邮件服务了,咱们改用阿里云的或者本身的邮件服务,那么,你不得不修改 Comment::init() 里面对 $_eMailSender 的实例化语句:$this->_eMailSender = new GmailSender(); 不但违反了OO思想的开闭原则并且对于复用性、维护性、测试、扩展都带来了不便,不是不便并且很是不方便。 依赖注入就是为了解决这个问题而生的,固然,DI也不是惟一解决问题的办法,毕竟条条大路通罗马。 Service Locator也是能够实现解耦的。this

//构造函数注入、属性输入
class Comment extends IComment{
	private $_eMailSender;

	public function __construct($eMailSender){
		$this->_eMailSender = $eMailSender;
	}

	//当有新评论,即save()方法被调用以后中,会触发该方法
	public function afterInsert(){
		$this->_eMailSender->send(...$param);
		...
	}
}

//实例化两种不一样的邮件服务,固然,他们都实现了EmailSenderInterface
$sender1 = new GmailSender();
$sender2 = new MyEmailSender();

//用构造函数将GamilSender注入
$comment1 = new Comment($sender1);
//使用Gmail发送邮件
$comment1->save();

//用构造函数将MyEmailSender注入
$comment2 = new Comment($sender2);
//使用MyEmailSender发送邮件
$comment2->save();

上面代码对比原来代码,解决了Comment类对于Gmail等具体类的依赖,将死的代码成活的的了(专业术语为:编译时转为运行时)。这种思想充分的展现了扩展性须要什么邮件服务类写出新的类便可、灵活性编译时转为运行时、复用性不论什么项目只要用到了邮件模块就可使用,邮件模块具备独立性。阿里云

依赖注入容器设计

从上面DI两种注入方式来看,依赖单元的实例化代码是一个重复、繁琐的过程。 能够想像,一个Web应用的某一组件会依赖于若干单元,这些单元又有可能依赖于更低层级的单元, 从而造成依赖嵌套的情形。code

参考文献:component

依赖注入和依赖注入容器