依赖倒置原则 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