🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## **依赖注入** 允许类的使用者为这个类注入依赖行为,通常情况下,这些依赖表现为对象、闭包或者回调方式;依赖注入的的思想就像它只给你家空调的遥控器提供电池,遥控器不会关心电池是哪个牌子、什么类型的牌子,你只需要满足遥控器电池的尺寸和电压 >[info]依赖注入关键:接口、多态、构造函数注入 将一个对象传入另一个对象中(将对象以new时的构造函数或者方法传入另一个类保存) **目的**:**为了提升组件重用的频率** 希望日志类可以使用任意一个数据存储引擎,即这个日志类依赖数据存储引擎但不关心这些引擎怎么实现的 ``` <?php class Log{ protected $engine=false /** * 组装日志信息并交给选定的日志引擎写入日志 * @param [type] $message [description] */ public function add($message){ if(!$this->engine){ throw new Exception("引擎不存在无法写入日志"); } $data['database']=time(); $data['message']=$message $session=Registry::get('session'); $data['user']=$message->getUSerID(); $this->engine->add($data); } /** * 设置日志引擎(文件、redis、数据库等,本例以文件引擎为例) * @param Log_Engine_Interface $engine [description] */ public function setEngine(Log_Engine_Interface $engine){ $this->engine=$engine; } public function getEngine(){ return $this->engine; } } /** * 存储引擎接口 */ interface Log_Engine_Interface{ /** * 添加日志事件 * @param array $data 事件内容 */ public function add(array $data); } /** * 文件存储引擎 */ class Log_Engine_File implements Log_Engine_Interface{ /** * 日志写入 * @param array $data [description] */ public function add(array $data){ $line='['.data('r',$data['datetime']).']'.$data['message'].'User:'.$data['user'].PHP_EOL $config=Registry::get('site-config'); if(!file_put_contents($config['location'],$line,$flags=FILE_APPEND)){ throw new Exception("写入出错"); } } } $engine=new Log_Engine_File(); $Log=new Log(); $log->setEngine($engine); $Registry::add($log); ``` 常规的想在注册完成后发送一个邮件的写法 ``` //1 Email.class.php发送邮件的类 class Mail{ public function send(){ //发送邮件的具体代码 } } //2底层代码: 由注册的类'Register.clas10s.php' 里调用send()发送 class Register{ private $_emailObject; public function doRegister(){ //这里是如何注册 $this->$_emailObject = new Mail();//对象 $this->$_emailObject->send();//发送邮件(连续调用) } } //3上层代码: 某个注册页面: include 'Mail.class.php'; include 'Register.class.php'; $reg = new Register(); $reg->doRegister(); ``` xxx天过后,产品人员说发送邮件的不好,要使用发送短信的,然后你说这简单我把'Mail'类改下 然后你必须将底层代码Register类的doRegister()里的内容`new Mail()`修改为`new Message()` 怎么解决了'Register'对信息发送类的依赖呢? 利用接口的多态性 使用构造函数注入的方法,使得它只依赖于发送短信的接口,只要实现其接口中的'send'方法,不管你怎么发送都可以(除了构造函数注入,还有方法注入及属性注入 ) ``` //修改后 //1 Mail.class.php各种方式发送消息的类 interface Mail{ public function send();//接口类的方法全是公共public 的抽象方法 子类必须全部继承接口的父类 } class Email implements Mail{ public function send(){ //发送email } } class SmsMail implements Mail{ public function send(){ //发送短信 } } //2然后又注册后又要发送消息的类'Register.class.php' class Register{ private $_mailObj; public function __construct(Mail $mailObj){ $this->_mailObj=$mailObj;//初始化发送方式 } public function doRegister(){ $this->_mailObj->send();//发送消息 } } //3 注册页面 function run(){ $reg = new Register();//实例化注册类 $emailObj = new Email(); $smsMail = new SmsMail(); $reg->doRegister($emailObj);//用email发送 $res->doRegister($smsMail);//用短信发送 } run(); //较好的封装 class Client{ public static function main(){ $mother = new Register(); $emailObj = new Email(); $smsMail = new SmsMail(); $mother->narrate(emailObj); $mother->narrate(smsMail); } } $client = new Client(); $client->main(); ``` 上面的代码解决了'Register'对信息发送类的依赖,使用构造函数注入的方法,使得它只依赖于发送短信的接口,只要实现其接口中的'send'方法, 注意在这里上面的必须先引入信息发送类在引入注册类 怎么解决引入加载顺序呢,这就需要用到依赖倒置啦,上面例子很好的实现了该思想,实例化注册类Register的时候加上了类型限制,如果传入的是信息发送类则实例化成功否则会报错 **依赖倒置原则**(`Dependence Inversion Principle` `DIP`)思想的定义是“**高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象** 传统软件设计中,上层代码依赖于下层代码,当下层出现变动时, 上层代码也要相应变化,维护成本较高。而DIP的核心思想是上层定义接口,下层实现这个接口, 从而使得下层依赖于上层,降低耦合度,提高整个系统的弹性。这是一种经实践证明的有效策略。 ***** ***** 依赖注入(DI)和控制反转(IOC)基本是一个意思,因为说起来谁都离不开谁。 **依赖注入(DI):** 简单来说,a依赖b,但a不控制b的创建和销毁,仅使用b,那么b的控制权交给a之外处理,这叫控制反转(IOC),而a要依赖b,必然要使用b的instance,那么 >[info] 通过a的接口,把b传入; >通过a的构造,把b传入; > 通过设置a的属性,把b传入; 这个过程叫依赖注入(DI)。 **IOC Container:** 随着依赖注入(DI)的频繁使用,要实现控制反转(IOC),会有很多重复代码,甚至随着技术的发展,有更多新的实现方法和方案,那么有人就把这些实现控制反转(IOC)的代码打包成组件或框架,来避免人们重复造轮子。 所以实现控制反转(IOC)的组件或者框架,我们可以叫它IOC Container。 大多数面向对象编程语言,在调用一个类的时候,先要实例化这个类,生成一个对象。 如果你在写一个类,过程中要调用到很多其它类,甚至这里的其它类,也要“依赖”于更多其它的类,那么可以想象,你要进行多少次实例化。这就是“依赖”的意思。 依赖注入,全称是“依赖注入到容器”, 容器(IOC容器)是一个设计模式,它也是个对象,你把某个类(不管有多少依赖关系)放入这个容器中,可以“解析”出这个类的实例。 所以依赖注入就是把有依赖关系的类放入容器(IOC容器)中,然后解析出这个类的实例。仅此而已 推荐一篇博文:[依赖注入和控制反转的理解,写的太好了](https://blog.csdn.net/liunianqingshi/article/details/78144489)