依赖倒置原则 DIPDependence Inversion Principle

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

控制反转(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提供了动态地创建、注入依赖单元,映射依赖关系等功能,减少了许多代码量。

服务定位器(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也是可以实现解耦的。

//构造函数注入、属性输入
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应用的某一组件会依赖于若干单元,这些单元又有可能依赖于更低层级的单元, 从而形成依赖嵌套的情形。

参考文献:

依赖注入和依赖注入容器

相关文章

迭代器模式(Iterator)迭代器模式(Iterator)[Cursor]意图...
高性能IO模型浅析服务器端编程经常需要构造高性能的IO模型,...
策略模式(Strategy)策略模式(Strategy)[Policy]意图:定...
访问者模式(Visitor)访问者模式(Visitor)意图:表示一个...
命令模式(Command)命令模式(Command)[Action/Transactio...
生成器模式(Builder)生成器模式(Builder)意图:将一个对...