当前位置: 首页 > 后端技术 > PHP

【现代PHP专题(三)】依赖注入与服务容器

时间:2023-03-29 15:19:10 PHP

DependencyInversionandInversionofControl是一种编程思想,而DependencyInjection就是实现这种面向接口或者抽象面向编程的思想和概念理解DependencyInversion通过服务容器依赖倒置原则是一种软件设计思想。在传统软件中,上层代码依赖于下层代码。当下层代码发生变化时,上层代码也必须随之变化,维护成本高。依赖倒置原则的思想是上层不应该依赖下层,而应该依赖接口。即上层代码定义接口,下层代码实现接口,使下层依赖于上层接口,降低耦合,提高系统灵活性。InversionofControl当调用者需要被调用者的协助时,在传统的编程过程中,通常是调用者创建一个被调用者的实例,但是在这里,创建被调用者实例的工作不再由调用者来完成,而是创建被调用者的移动到调用者之外,从而反转被调用者。调用者的创建消除了调用者对被调用者创建的控制,因此称为控制反转。要实现控制反转,通常的解决方案是将创建callee实例的工作交给IoC容器,然后将callee注入到caller中(通过constructor/methodinjection),这样我们就实现了caller和caller的解耦被调用者称为依赖注入。依赖注入不是目的,它是一系列的工具和手段,最终目的是帮助我们开发松耦合(loosecoupled)、可维护、可测试的代码和程序。实现这一原则的方法是众所周知的面向接口或面向抽象的编程。通俗地说,调用对象的方法时,首先要实例化该对象。所谓注入,就是工厂模式的升华。通过一个更高层的工厂(容器)来完成对象的实例化,实现调用者和被调用者的解耦。解决什么问题实现调用者和被调用者的解耦[info]所谓上层代码依赖接口,即业务逻辑的实现跳过了具体对象的抽象行为。比如我们要给用户发消息,我们可以通过邮件或者短信的方式发送。上层代码不需要关注它用什么发送,发送就可以了(适配器模式).PHP_EOL;}}classSmsMail实现Mail{publicfunctionsend(){echo'SendSMS'.PHP_EOL;}}//注册容器类Register{private$_mailObj;//构造函数已经约束它必须是实现Mail接口的类的实例publicfunction__construct(Mail$mailObj){$this->_mailObj=$mailObj;}publicfunctiondoRegister(){//必须有发送方法$this->_mailObj->send();//发送信息}}$emailObj=newEmail();$smsObj=newSmsMail();$reg=newRegister($emailObj);$reg->doRegister();//使用email发送$reg=newRegister($smsObj);$reg->doRegister($smsObj);//使用构造函数注入的方法发送短信,使其只依赖于发送短信的接口,只要在其接口中实现'发送'方法,就可以通过任何方式发送。上面通过构造函数注入对象的方法是最简单的依赖注入;当然,“注入”不仅可以通过构造函数注入,还可以通过属性注入。在上面,您可以通过"setter"为属性"mailObj"动态分配一个值。通过php反射机制自动注入真正的依赖注入容器会提供更多的特性,比如自动绑定(Autowiring)或自动解析(AutomaticResolution)注解解析器(Annotations)延迟注入(Lazyinjection)c=$c;}publicfunctiondoSomething(){$this->c->doSomething();echo__METHOD__,'我是周伯通B|';}}A类{私人$b;公共函数__construct(B$b){$this->b=$b;}publicfunctiondoSomething(){$this->b->doSomething();echo__METHOD__,'我是周伯通A|';}}classContainer{private$s=[];公共函数__set($k,$c){$this->s[$k]=$c;}publicfunction__get($k){return$this->build($this->s[$k]);}/***自动绑定自动装配自动解析*@paramstring$className*@returnobject*@throwsException*/publicfunctionbuild($className){//如果是匿名函数(Anonymousfunctions),也称为闭包函数(closure)if($classNameinstanceofClosure){//执行闭包函数,并返回结果$className($this);}if(!class_exists($className)){thrownewException("{$className}类不存在");}/**@varReflectionClass$reflector*/$reflector=newReflectionClass($className);//检查类是否可以实例化,排除抽象类abstract和对象接口interfaceif(!$reflector->isInstantiable()){thrownewException("Can'tinstantiatethis.");}/**@varReflectionMethod$constructor获取类的构造函数*/$constructor=$reflector->getConstructor();//如果没有构造函数,直接实例化返回,(注意!这里退出递归1)if(is_null($constructor)){returnnew$className;}//取构造函数参数,通过ReflectionParameter数组返回参数列表$parameters=$constructor->getParam酯();//递归解析构造函数的参数$dependencies=$this->getDependencies($parameters);//创建类的新实例,给定的参数将传递给类的构造函数返回$reflector->newInstanceArgs($dependencies);}/***@paramarray$parameters*@returnarray*@throwsException*/publicfunctiongetDependencies($parameters){$dependencies=[];/**@varReflectionParameter$parameter*/foreach($parametersas$parameter){/**@varReflectionClass$dependency*/$dependency=$parameter->getClass();if(is_null($dependency)){//是一个变量,有默认值设置默认值(注意,这里退出递归2)$dependencies[]=$this->resolveNonClass($parameter);}else{//是类,递归解析$dependencies[]=$this->build($dependency->name);}}返回$dependencies;}/***@paramReflectionParameter$parameter*@returnmixed*@throwsException*/publicfunctionresolveNonClass($parameter){//如果有默认值则返回默认值if($parameter->isDefaultValueAvailable()){返回$parameter->getDefaultValue();}thrownewException('我不知道该做什么。');}}/*//example1$container=newContainer();$container->b='B';$container->a=function($container){returnnewA($container->b);};//从容器中获取A$model=$container->a;//输出:C::doSomething我是周伯通C|B::doSomething我是周伯通B|A::doSomething我是周伯通A|//实现自动依赖注入$model->doSomething();*///example2$di=newContainer();$di->php7='A';//自动注入classA/**@varA$php7*/$foo=$di->php7;$foo->doSomething();//C::doSomething我是周伯通C|B::doSomething我是周伯通B|A::doSomething我是周伯通A|参考:https://www.cnblogs.com/pains...https://www.cnblogs.com/phppe...