php全栈之路教程(PHP进阶学习之依赖注入与Ioc容器详解)
php全栈之路教程
PHP进阶学习之依赖注入与Ioc容器详解本文实例讲述了php依赖注入与ioc容器。分享给大家供大家参考,具体如下:
背景
在很多编程语言(例如java)开发中,程序员在某个类中需要依赖其它类的方法,则通常是new一个依赖类再调用类实例的方法,这种开发存在的问题是new的类实例不好统一管理,一旦有修改,牵扯的类会很多。
最早在java的spring提出了依赖注入的思想,即依赖类不由程序员实例化,而是通过spring容器帮我们new指定实例并且将实例注入到需要该对象的类中。目前许多主流php框架也使用了依赖注入容器,如thinkphp、laravel等。
一、概念
1、容器:字面上理解就是装东西的东西。常见的变量、对象属性等都可以算是容器。一个容器能够装什么,全部取决于你对该容器的定义。当然,现在我们讨论的是这样一种容器,它存放的不是文本、数值,而是对象、对象的描述(类、接口)或者是提供对象的回调(闭包),通过这种容器,我们得以实现许多高级的功能,其中最常提到的,就是 “解耦”、“依赖注入”。
2、ioc - inversion of control 控制反转
控制反转是从容器的角度在描述,即:容器控制应用程序,由容器反向的向应用程序注入应用程序所需要的外部资源。
3、di - dependency injection 依赖注入
依赖注入是从应用程序的角度在描述,可以把依赖注入,即:应用程序依赖容器创建并注入它所需要的外部资源。
备注:依赖注入和控制反转说的是同一个东西,是一种设计模式,这种设计模式用来减少程序间的耦合,从某个方面讲,就是它们描述的角度不同。
二、依赖注入的原理
一般情况下,当存在类与类之间的依赖关系的时候,我们都是通过直接实例化的方式进行调用。一旦出现多层依赖,这种方式的耦合程度就很高,在需要修改其中一个类的时候,会牵扯很多依赖它的类的修改,因此对代码的改动会比较大。
下面简单举一个a->b->c三层依赖的关系解释怎么运用依赖注入来解耦,提高开发效率。
而依赖注入方式如下:
解析:
常规写法里面,一旦c类需要作出改变,或者b类的调用需要改变成d类的时候,还需要考虑到依赖自己的b类,即还需要对b类作出修改。
依赖注入的思想就是即用即实例,反转类与类之间的控制关系,实现由调用类a类控制后续的依赖关系,这样可以让b类随意的更改所需依赖和实例化的类(c类或d类),达到解耦的目的。
三、常用的依赖注入方式:
1、构造方法注入;2、set属性注入;3、静态工厂方法注入;
上述的例子使用的就是构造方法注入的方式,将对象作为参数传递到构造方法中;同样的set属性注入也是相类似的方法,不同的仅仅是在set一个类的成员的属性时传递这个对象参数,在此就不一一举例了。
除此之外,还有静态工厂方法注入的方式,这种方法与静态工厂方法类似。
我们知道静态工厂方法就是通过一个类来管理需要实例化的多个相似的类,该类会定义一个方法用于获取需要实例化的对象,而具体要实例化哪个对象就依赖于传递进来的对象名参数了。
对于静态工厂方式的注入,与一般的静态工厂方法不同之处在于这个传进来的参数是一个已经实例化过的对象。
|
<?php class ioc { protected static $registry = []; public static function bind( $name , callable $resolver ) //传入类名和类对象实例 { static :: $registry [ $name ] = $resolver ; } public static function make( $name ) //静态工厂方法 { if (isset( static :: $registry [ $name ])) { $resolver = static :: $registry [ $name ]; return $resolver (); //实例化 } throw new exception( 'alias does not exist in the ioc registry.' ); } } |
总而言之,三种方式传递的都是实例化对象,只是不同之处在于传递的位置分别为构造方法、set属性、静态工厂方法而已。
四、依赖注入容器(ioc容器)
大多数时侯,在使用依赖注入方式解耦组件时,并不需要用到容器。
当一段程序需要实例化的类太多或者依赖太多的时候,重复依赖注入的代码是比较繁琐的事情,例如以下情况:
当产生以上关系的时候,依赖注入的代码会比较混乱,而且存在重复,更有可能在调用一个一般方法时new一个不需要的类,产生冗余。
此时需要使用容器,使用依赖注入容器后的思路是应用程序需要到a类,就从容器内取得a类。具体是容器创建c类,再创建b类并把c注入,再创建a类,并把b类注入,应用程序调用a类方法, a类调用b类方法,接着做些其它工作.总之容器负责实例化,注入依赖,处理依赖关系等工作。
对于实际开发中复杂多变的代码环境,我们并不能完全知道现在的类在未来会扩展成什么情况,因此我们需要在有新的依赖类加入的时候,通过容器去实现实例化该类的方法。因此,在实例化未知类的时候,最能探索一个类的内部结构和实例化的方法就是利用反射,由此可知,反射是容器管理各个依赖类的核心。我们可以通过实例来了解容器的内部实现:
三个存在依赖关系的类:文件testclass.php
|
<?php //依赖关系:company->department->group class group { public function dosomething() { echo __class__ . ":" . 'hello' , '|' ; } } class department { private $group ; public function __construct(group $group ) { $this ->group = $group ; } public function dosomething() { $this ->group->dosomething(); echo __class__ . ":" . 'hello' , '|' ; } } class company { private $department ; public function __construct(department $department ) { $this ->department = $department ; } public function dosomething() { $this ->department->dosomething(); echo __class__ . ":" . 'hello' , '|' ; } } |
ioc容器的内部实现:
|
<?php class container { private $s = array (); public function __set( $k , $c ) { $this ->s[ $k ] = $c ; } public function __get( $k ) { return $this ->build( $this ->s[ $k ]); } /** * 自动绑定(autowiring)自动解析(automatic resolution) * * @param string $classname * @return object * @throws exception */ public function build( $classname ) { // 如果是匿名函数(anonymous functions),也叫闭包函数(closures) if ( $classname instanceof closure) { // 执行闭包函数,并将结果 return $classname ( $this ); } /*通过反射获取类的内部结构,实例化类*/ $reflector = new reflectionclass( $classname ); // 检查类是否可实例化, 排除抽象类abstract和对象接口interface if (! $reflector ->isinstantiable()) { throw new exception( "can't instantiate this." ); } /** @var reflectionmethod $constructor 获取类的构造函数 */ $constructor = $reflector ->getconstructor(); // 若无构造函数,直接实例化并返回 if ( is_null ( $constructor )) { return new $classname ; } // 取构造函数参数,通过 reflectionparameter 数组返回参数列表 $parameters = $constructor ->getparameters(); // 递归解析构造函数的参数 $dependencies = $this ->getdependencies( $parameters ); // 创建一个类的新实例,给出的参数将传递到类的构造函数。 return $reflector ->newinstanceargs( $dependencies ); } /** * @param array $parameters * @return array * @throws exception */ public function getdependencies( $parameters ) { $dependencies = []; /** @var reflectionparameter $parameter */ foreach ( $parameters as $parameter ) { /** @var reflectionclass $dependency */ $dependency = $parameter ->getclass(); if ( is_null ( $dependency )) { // 是变量,有默认值则设置默认值 $dependencies [] = $this ->resolvenonclass( $parameter ); } else { // 是一个类,递归解析 $dependencies [] = $this ->build( $dependency ->name); } } return $dependencies ; } /** * @param reflectionparameter $parameter * @return mixed * @throws exception */ public function resolvenonclass( $parameter ) { // 有默认值则返回默认值 if ( $parameter ->isdefaultvalueavailable()) { return $parameter ->getdefaultvalue(); } throw new exception( 'i have no idea what to do here.' ); } } require_once "./testclass.php" ; //开始测试,先测试已知依赖关系的情况 $c = new container(); $c ->department = 'department' ; $c ->company = function ( $c ) { return new company( $c ->department); }; // 从容器中取得company $company = $c ->company; $company ->dosomething(); //输出: group:hello|department:hello|company:hello| // 测试未知依赖关系,直接使用的方法 $di = new container(); $di ->company = 'company' ; $company = $di ->company; $company ->dosomething(); //输出: group:hello|department:hello|company:hello| |
我们可以通过一张图解释ioc容器的内部逻辑:
五、总结
ioc的基本概念是:不创建对象,但是描述创建它们的方式。在代码中不直接与对象和服务连接,但在配置文件中描述哪一个组件需要哪一项服务。spring容器负责将这些联系在一起。也就是说,spring的ioc负责管理各种对象的创建、清除以及它们之间的联系。
希望本文所述对大家php程序设计有所帮助。
原文链接:https://blog.csdn.net/dream_successor/article/details/79078905
- 织梦cms漏洞怎么解决(织梦cms、帝国cms、PHPcms优缺点解析)
- php安全攻防(phpstudy linux小皮面板怎么防cc攻击)
- php 上传临时文件扩展名(浅析PHP 中move_uploaded_file 上传中文文件名失败)
- php框架使用方法大全(PHP MVC框架中类的自动加载机制实例分析)
- 织梦cms中的模块怎么设置(织梦CMS模板在runphp=yes的标签中调用其他field值的方法)
- thinkphp框架实例(ThinkPHP框架整合微信支付之JSAPI模式图文详解)
- php用什么解压(php解压缩zip和rar压缩包文件的方法)
- thinkphp框架多文件上传完整代码(Thinkphp5 自定义上传文件名的实现方法)
- python与php(解决Python3 被PHP程序调用执行返回乱码的问题)
- thinkphp5开发小程序后台(PHP小程序支付功能完整版基于thinkPHP)
- php 精度误差调整(php精度计算的问题解析)
- php数组练习题答案(PHP 数组黑名单/白名单实例代码详解)
- php封装api(PHP常用的类封装小结4个工具类)
- 在php中与数据库连接的技术(PHP7.0连接DB操作实例分析基于mysqli)
- thinkphp5.1插件实现(Thinkphp5.0框架使用模型Model的获取器、修改器、软删除数据操作示例)
- php的数据类型约束的好处(PHP中的Iterator迭代对象属性详解)
- 学好汉语拼音,从娃娃绕口令抓起,平时还是要多练 收藏好(从娃娃绕口令抓起)
- 仙女们的私藏鲜法大PK 鲜香切块牛肉(仙女们的私藏鲜法大PK)
- 天热没胃口 这道菜开胃又下饭,2个小技巧新手一学就会(这道菜开胃又下饭)
- 指天椒紫苏爆炒牛肉(指天椒紫苏爆炒牛肉)
- 谷雨前,吃牛羊肉别忘了吃河鲜,除湿还清热,加紫苏一炒特解馋(吃牛羊肉别忘了吃河鲜)
- 紫苏牛肉锅里滚一滚,香的鼻子都要掉了(紫苏牛肉锅里滚一滚)
热门推荐
- python入门知识点总结(深入解析Python小白学习操作列表)
- html5清除浮动的方法(HTML5实现移动端点击翻牌功能)
- sqlserver存储过程同步数据(SQL Server存储过程同时返回分页结果集和总数)
- python好看图案(python实现趣味图片字符化)
- SQL Server数据库应用中有用的几个工具
- ftp上传文件权限设置(FTP二进制上传是什么意思?设置并使用二进制BINARY上传?)
- 云服务器的安全问题(云服务器如何提高安全组性能?)
- docker部署php本地开发环境(CentOS7环境下使用Docker搭建PHP运行环境的过程详解)
- docker如何搭建mysql(docker容器访问宿主机的MySQL操作)
- 织梦dedecms自定义表单选项必填修改解决方法(织梦dedecms自定义表单选项必填修改解决方法)
排行榜
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9