原文:博客,转载请注明出处。本文代码:GitHub前言服务容器是Laravel框架实现模块化解耦的核心。模块化就是将系统拆分成多个子模块,子模块之间的耦合度尽可能低,代码中尽量避免直接调用。只有这样,才能提高系统的代码重用性、可维护性和可扩展性。下面的出行示例包括两种出行方式:火车和飞机,相应地,给出耦合度越来越低的三种实现:高耦合实现、工厂模式解耦、IOC模式解耦。高耦合的实现代码实现了TrafficTool接口的定义并用Train和Plane实现,最后在Traveler中实例化出行工具并走开。代码非常简洁:travelTool=newTrain();}publicfunctiontravel(){$this->travelTool->go();}}$me=newTraveler();$我->旅行();run:$phpnormal.php[TravelBy]:train的好处是代码非常简洁:最后直接调用一个接口两个类。缺点是在第32行,Traveler和Train这两个组件是耦合的。以后如果想坐飞机旅行,必须修改__construct()的内部实现:$this->travelTool=newPlane();可重用性和可维护性差:在实际的软件开发中,代码会根据业务需要不断变化,不断修改。如果组件之间直接相互调用,则不能轻易修改组件的代码,以免调用的地方出错。工厂模式解耦工厂模式将代码的常量部分和可变部分分开,从而在不同的条件下创建不同的对象。代码实现...classTrafficToolFactory{publicfunctioncreate($name){switch($name){case'train':returnnewTrain();case'plane':returnnewPlane();默认值:exit('[无交通工具]:'。$name);}}}//旅游类,乘火车旅行类Traveler{protected$trafficTool;publicfunction__construct($toolName){//使用工厂类实例化需要的交通工具$factory=newTrafficToolFactory();$this->travelTool=$factory->create($toolName);}publicfunctiontravel(){$this->travelTool->go();}}//传入指定方法$me=newTraveler('train');$我->旅行();运行:$phpfactory.php[TravelBy]:train优点提取修改的部分代码:改变交通工具,乘坐飞机出行,直接修改$me=newTraveler('plane')即可。适用于简单的要求。缺点是仍然没有完全解决依赖关系:现在Traveler和TrafficToolFactory有依赖关系。当需求增加时,工厂的switch...case等代码不好维护。IOC模式解耦IOC是InversionOfControll的缩写,即控制反转。这里的“倒置”可以理解为是指外部管理组件之间的依赖关系。简单的依赖注入依赖注入是IOC的一种实现,意思是直接以外部参数(接口)的形式注入组件之间的依赖关系。例如,进一步解耦上面的工厂模式:}}classPlaneimplementsTrafficTool{publicfunctiongo(){echo'[TravelBy]:plane',PHP_EOL;}}classTraveler{protected$trafficTool;//参数$tool是控制反转中要反转的部分,直接传入依赖对象就可以了//以后新的工具如Car,GetWay...也会被实例化传入参数直接调用publicfunction__construct(TrafficTool$tool){$this->trafficTool=$tool;}publicfunctiontravel(){$this->trafficTool->go();}}$train=newTrain();$me=newTraveler($train);//直接将依赖项作为参数注入$me->travel();run:$phpsimple_ioc.php[TravelBy]:trainAdvancedDependencyInjection简单注入的问题如果三个人开车旅行,坐飞机,或者坐高铁出门,那么你的代码可能是这样的:$火车=新火车();$plane=newPlane();$car=newCar();$a=newTraveler($car);$b=newTraveler($plane);$c=newTraveler($train);$a->旅行();$b->旅行();$c->旅行();看起来像两个字:兰寿。与工厂模式相比,上面的简单依赖注入解耦了很多。参考Laravel中服务容器的概念,可以继续解耦。会用到PHP反射和匿名函数,参考:Laravel框架常用的PHP语法IOC容器高级依赖注入=简单依赖注入+IOC容器binds[$abstract]=$concrete;}else{$this->instances[$abstract]=$concrete;}}/***生产:执行回调函数**@param$abstract字符指令*@paramarray$paramscallback函数需要的参数*@returnmixed回调函数的返回值*/publicfunctionmake($abstract,$params=[]){if(isset($this->instances[$abstract])){return$this->instances[$abstract];}//此时$this是一个有2个元素的数组//Array(//[0]=>容器对象(//[binds]=>Array(...)//[instances]=>Array()//)//[1]=>"train"//)array_unshift($params,$这个);//将参数传递给回调函数returncall_user_func_array($this->binds[$abstract],$params);}}$container=newContainer();$container->bind('traveler',function($container,$trafficTool){returnnewTraveler($container->make($trafficTool));});$container->bind('train',function($container){returnnewTrain();});$container->bind('plane',function($container){returnnewPlane();});$me=$container->make('traveler',['train']);$me->travel();run:$phpadvanced_ioc.php[TravelBy]:train简化解耦代码三人再次出去玩,代码会简化为:$a=$container->make('traveler',['car']);$b=$container->make('旅行者',['火车']);$c=$container->make('旅行者',['飞机']);$a->旅行();$b->旅行();$c->旅行();更多参考:Laravel的服务容器是一个神奇的服务容器。Laravel自带的服务容器是一个更高级的IOC容器。其简化代码如下:getClosure($abstract,$concrete);}$this->binds[$abstract]=compact('concrete','shared');}//获取回调函数“构建”:“制作”;返回$container->$method($concrete);};}protectedfunctiongetConcrete($abstract){if(!isset($this->binds[$abstract])){return$abstract;}返回$this->binds[$abstract]['concrete'];}//生成实例对象publicfunctionmake($abstract){$concrete=$this->getConcrete($abstract);如果($this->isBuildable($abstract,$concrete)){$obj=$this->build($concrete);}else{$obj=$this->make($concrete);}返回$obj;}//判断是否使用反射来实例化保护函数isBuildable($abstract,$concrete){return$concrete==$abstract||$具体闭包实例;}//通过反射实例化$concrete的对象publicfunctionbuild($concrete){if($concreteinstanceofClosure){return$concrete($this);}$reflector=newReflectionClass($concrete);if(!$reflector->isInstantiable()){echo"[无法实例化]:".$混凝土;}$constructor=$reflector->getConstructor();//使用默认构造函数if(is_null($constructor)){returnnew$concrete;}$refParams=$constructor->getParameters();$实例=第$is->getDependencies($refParams);返回$reflector->newInstanceArgs($instances);}//获取实例化对象所需的参数publicfunctiongetDependencies($refParams){$deps=[];foreach($refParamsas$refParam){$dep=$refParam->getClass();}如果(is_null($dep)){$deps[]=null;}else{$deps[]=$this->resolveClass($refParam);}}返回(数组)$deps;}//获取参数的类型类名publicfunctionresolveClass(ReflectionParameter$refParam){return$this->make($refParam->getClass()->name);}}$container=newContainer();//将旅行者连接到火车$container->bind('TrafficTool','Train');$container->bind('traveller','Traveller');//创建旅行者实例$me=$container->make('traveller');$我->旅行();run:$phplaravel_ioc.php[TravelBy]:trainTrain类要被实例化需要先注册到容器中,这涉及到Laravel中ServiceProvider的概念至于服务提供者如何注册类,注册后如何实例化,实例化后如何调用……下一节会详细分析。综上所述,本文以游记demo介绍三种实现方式:直接实现高耦合、工厂模式解耦和IOC模式解耦。耦合度越来越低,最后实现了一个简化版的Laravel服务容器。Laravel的美妙之处在于开发的组件解耦,这离不开服务容器和服务提供者的概念。下一篇文章将使用Laravel框架中的Container类laravel/framework/src/Illuminate/Container.php梳理一下Laravel服务容器的工作流程。
