PHP中依赖注入和控制反转的实现(以CI框架为例)暨 CI框架(CodeIgniter)使用技巧和步骤小结(六)

作者:掠影
最后编辑时间:2018-10-19 11:56:36
浏览次数:810



CI框架(CodeIgniter)使用技巧和步骤小结(六)

起因

最近在日常使用框架时,遇到了对象实例化相关的问题;特别是CI框架中对于涉及对象的实例化的保存和调用的相关问题在上文CI框架(CodeIgniter)使用技巧和步骤小结(五)中文末有提到,故而本文在上节基础上,通过分析CI中Loader类的实现思路,同时对依赖注入(DI)和控制反转(IOC)的相关概念和实现进行分析和介绍

分析

  • 依赖注入和控制反转的概念一直都有被人们提及,实际上,在编程实践中我们时常会用到,但是一直意识不到而已;以IOC为例,最早进行OOP的时候,在对象调用成员时,我们时常会用到例如$obj->sub = new Obj();之类的操作,即对成员变量进行初始化;
  • 这一操作当然没有什么问题,也是诸多新手时常用到的;不过对于OO思想来说,这一点是相当不友好的,由于每一个对象的创建和调用时都会进行这样的一次new Obj()的操作,会进行大量的内存分配操作,同时对象间的耦合关系是相当强的(强耦合),一旦被new的对象发生了变化或者被更新,废弃时,需要进行大量代码更改操作,对全局的$obj->sub = new Obj()都需要替换成$obj->sub = new ObjNew();
  • 故而我们想到,是不是可以有一类操作,可以使得我们在进行对象调用的时候,不需要手动实例化,而是通过一个外部的操作将这一对象实例传入,从而解决了这一依赖关系:由调用者实例化-》外部实例化传入,即将实例的控制权交由外部处理,调用者只负责调用,这一过程就叫做控制反转(IOC)
  • 而实现这一控制反转的方式,就是依赖注入,即不需要手动new一个实例,而是通过外部实例传入(注入),从而建立依赖关系;在逻辑关系上来看,控制反转是一种设计思想,用于降低耦合+减少内存占用;而依赖注入则是实现这种思想的方式;例如
function(Obj $outer_sub){             
   $obj->sub = $outer_sub;             
   $obj->sub->do_something();         
}

这里通过外部传入了一个实例化变量,对内部成员进行了赋值,从而建立起两个实例间的依赖关系

实现

上节里介绍了DI和IOC的来源和思路,本节就通过对CI框架里Loader加载器的源码分析,深入地理解这两者在CI中的实现思路;

  • 文件位置:core/ Loader.php, 类名:CI_Loader
  • 用途:加载所有框架和代码中声明的库,模型,包文件和Helper文件,并保存在一个全局容器中
  • 核心函数:
    • initialize(): 初始化加载器,获取配置文件里各项需要加载的内容,并调用_ci_autoloader()
    • _ci_autoloader():
      • 分析各项需要加载的内容,利用不同的加载函数进行加载,并保存到一个全局容器中;
      • 这个容器就是所调用的Controller实例的load成员;由CodeIgniter.php中get_instance()获取;
      • 这个容器会保存所有已经实例化的各种对象,包括model,library等,从而在控制器中可以通过$this->load->library("xxx")或者$this->load->model("XXX")的方式来调用指定的实例(建立依赖关系), 即所说的DI容器;
    • _ci_init_class(): library对象的加载函数,遍历并判断容器中是否已经有实例化的指定library对象,若没有则将指定的lib类实例化并放入容器中
    • model(): model对象的加载函数,功能同上;只是这里加载的是Model而已;由于CI中Model是作为数据库表的映射+实现某些逻辑而存在,故而会加载一次数据库连接对象(同样保存在DI容器中)

总结:

  • 介绍了依赖注入和控制反转的思想,并介绍了CI框架中这一思想的实现方式;

    • 控制反转:OO的一种设计理念;
    • 依赖注入:控制反转的一种实现方式,传入实例而不需要手动创建
  • 分析了CI中Loader类的实现逻辑以及如何实现DI容器以保存外部对象,对不同的类型采用了不同的加载函数,从而将Model, Library, Helper[1]等不同的部件加载到全局容器中

附录

  • [1] Helper主要为各种函数定义,不需要(也不应该)实例化对象,故而Loader中的加载实际上只是通过文件包含的形式引入了对应文件的代码而已,并未进行实例化;Language文件也是如此
  • [2] Loader中不同的类型会调用不同的加载函数,使用时需要区别;同时在配置文件中声明的时候也需要注意:例如Helper中就不会实例化对象,很多问题都是从这里诞生的;
  • [3] 还欠Router和Output两篇,敬请期待

作者:萨秋(掠影)
转载请联系原作者
邮件:ryoalex@foxmail.com
QQ: 591647088

取消

感谢您的支持,我会继续努力的!关闭

扫码支持
大家有钱的捧个钱场,没钱的捧个人场233333

打开支付宝扫一扫,即可进行扫码打赏哦

分享到: QQ空间 更多



评论区