依赖倒置原则 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应用的某一组件会依赖于若干单元,这些单元又有可能依赖于更低层级的单元, 从而形成依赖嵌套的情形。
参考文献:
原文链接:https://www.f2er.com/javaschema/283219.html