1200字范文,内容丰富有趣,写作的好帮手!
1200字范文 > laravel 核心架构(1)服务容器-深入理解控制反转(IoC)和依赖注入(DI)

laravel 核心架构(1)服务容器-深入理解控制反转(IoC)和依赖注入(DI)

时间:2024-05-23 11:40:44

相关推荐

laravel 核心架构(1)服务容器-深入理解控制反转(IoC)和依赖注入(DI)

1. 介绍

laravel 容器

存放的 是对象、对象的描述(类、接口)或者是提供对象的回调,通过这种容器,我们得以实现许多高级的功能,其中最常提到的,就是 “解耦”、“依赖注入(DI)”。

服务容器的使用

2. 通过案例解析IoC和DI

多个超人多种能力

我们不应该手动在 “超人” 类中固化了他的 “超能力” 初始化的行为,而转由外部负责,由外部创造超能力模组、装置或者芯片等(我们后面统一称为 “模组”),植入超人体内的某一个接口,这个接口是一个既定的,只要这个 “模组” 满足这个接口的装置都可以被超人所利用,可以提升、增加超人的某一种能力。这种由外部负责其依赖需求的行为,我们可以称其为 “控制反转(IoC)”。

IoC设计思想:将依赖关系动态注入

工厂模式,依赖转移

工厂模式的缺点就是:

接口未知(即没有一个很好的契约模型,关于这个我马上会有解释)、产生对象类型单一。

总之就是,还是不够灵活。

虽然如此,工厂模式依旧十分优秀,并且适用于绝大多数情况。

class Superman4{protected $power;public function __construct(array $modules){// 1 单个能力,依赖// $this->power = new Power(999, 100);// 2 多个能力,依赖$this->power = new Flight(9, 100);// $this->power = new Force(45);// $this->power = new Shot(99, 50, 2);$this->power = [new Force(45),new Shot(99, 50, 2),];// 3 多个能力,工厂模式(依赖)$factory = new SuperModuleFactory();// 和之前区别不大,但没有那么多new关键字$this->power[] = $factory->makeModule('Flight', [9, 100]);// $this->power = $factory->makeModule('Force', [45]);// $this->power = $factory->makeModule('Shot', [99, 50, 2]);$this->power[] = array($factory->makeModule('Force', [45]),$factory->makeModule('Shot', [99, 50, 2]));// 4 工厂模式更改(依赖)// 由原来对多个外部的依赖变成了对一个 “工厂” 的依赖$factory = new SuperModuleFactory();foreach ($modules as $moduleName => $moduleOptions){$this->power[] = $factory->makeModule($moduleName, $moduleOptions);}}}

如果需要增加能力,还需要更改工厂类 ,噩梦般的感受!。。。

class SuperModuleFactory{public function makeModule($moduleName, $options){switch ($moduleName) {case 'Fight': return new Fight($options[0], $options[1]);case 'Force': return new Force($options[0]);case 'Shot': return new Shot($options[0], $options[1], $options[2]);// case 'more': .......// case 'and more': .......// case 'and more': .......// case 'oh no! its too many!': .......}}}

其实灵感就差一步!你可能会想到更为灵活的办法!

对,下一步就是我们今天的主要配角 —— DI (依赖注入)

由于对超能力模组的需求不断增大,我们需要集合整个世界的高智商人才,一起解决问题,不应该仅仅只有几个工厂垄断负责。

不过高智商人才们都非常自负,认为自己的想法是对的,创造出的超能力模组没有统一的接口,自然而然无法被正常使用。这时我们需要提出一种契约,这样无论是谁创造出的模组,都符合这样的接口

定义 规范/契约/接口

interface SuperModuleInterface{/*** 超能力激活方法** 任何一个超能力都得有该方法,并拥有一个参数*@param array $target 针对目标,可以是一个或多个,自己或他人*/public function activate(array $target);}

实现统一种方法且不同功能(或特性)的时候,会存在很多的类(class),这时候就需要有一个契约,即接口

可以被随时替换却不会产生影响的接口

只要不是由内部生产(比如初始化、构造函数 __construct 中通过工厂方法、自行手动 new 的),

而是由外部以参数或其他形式注入的,都属于依赖注入(DI)

依赖注入案例(手动创建并注入):

// 超能力模组$superModule = new XPower;// 初始化一个超人,并注入一个超能力模组依赖 (手动注入)$superMan = new Superman($superModule);

低效率产出(手动)怎么行? 需要自动化!

这种更为高级的工厂,就是工厂模式的升华 ——IoC 容器

class main{/*** main constructor.* 创建一个实例的同时解决其依赖关系,并且更加灵活。当有新的需求,只需另外绑定一个“生产脚本”即可。*/public function __construct(){// 创建一个容器$container = new Container;/*** 通过注册、绑定的方式向容器中添加一段可以被执行的回调(可以是匿名函数、非匿名函数、类的方法)* 作为生产一个类的实例的 脚本 ,* 只有在真正的 生产(make) 操作被调用执行时,才会触发。*/// 向 容器 添加 超人的生产脚本// 缺点,手动提供超人的模组参数 (更高级的Ioc容器可以自动化处理 - 反射实现)$container->bind('superman', function ($container, $moduleName){// moduleName 参数return new Superman($container->make($moduleName));});// 向 容器 添加 x射线能力的生产脚本$container->bind('xpower', function ($container){return new XPower;});// 向 容器 添加 炸弹能力的生产脚本$container->bind('ultrabomb', function ($container){return new UltraBomb;});// 开始启动生产$superman1 = $container->make('superman', 'xpower');$superman2 = $container->make('superman', 'ultrabomb');$superman3 = $container->make('superman', 'xpower');// ...随意添加}}

自动注入参数 - 反射

手动提供超人所需要的模组参数,但真正的 IoC

容器会根据类的依赖需求,自动在注册、绑定的一堆实例中搜寻符合的依赖需求,并自动注入到构造函数参数中去。

Laravel框架的服务容器正是这么做的。

这种自动搜寻依赖需求的功能,是通过反射(Reflection)实现的

3. Laravel 核心 - IoC 容器

功能模块

比如 Route(路由)、Eloquent ORM(数据库 ORM 组件)、Request(请求)以及 Response(响应)等,

都是与核心无关的类模块提供

这些类从注册到实例化,都是 Laravel 的服务容器负责的

服务提供者

一个类要被容器所能够提取,必须要先注册至这个容器

一个类需要绑定、注册至容器中,才能被“制造”

例如

需要某个服务,就得先注册、绑定这个服务到容器,

那么提供服务并绑定服务至容器的东西,就是服务提供者(Service Provider)。

服务提供者

主要分为两个部分,register(注册) 和 boot(引导、初始化)

class AppServiceProvider extends ServiceProvider{/*** Bootstrap any application services.* 引导、初始化** @return void*/public function boot(){//}/*** Register any application services.* 向容器注册"脚本"* 注册部分如果存在对未知事物的依赖,写在boot中** @return void*/public function register(){//}}

route静态访问

route类 get,post,any 等都不是静态(static)方法,

却能以静态方法访问

# vendor/laravel/framework/src/Illuminate/Routing/Router.php/*** Register a new GET route with the router.** @param string $uri* @param \Closure|array|string|null $action* @return \Illuminate\Routing\Route*/public function get($uri, $action = null){return $this->addRoute(['GET', 'HEAD'], $uri, $action);}

Route::get('/', function() {// bla bla bla...});

使用了门面(Facade)

模拟一个类,提供一个静态魔术方法__callStatic,

并将该静态方法映射到真正的方法上。

Route 类实际上是 Illuminate\Support\Facades\Route 通过 class_alias()

函数创造的别名而已,这个类被定义在文件

vendor/laravel/framework/src/Illuminate/Support/Facades/Route.php

<?php namespace Illuminate\Support\Facades;/*** @see \Illuminate\Routing\Router*/class Route extends Facade {/*** Get the registered name of the component.** @return string*/protected static function getFacadeAccessor(){return 'router';}}

这个类继承了一个叫做 Facade 的类

getFacadeAccessor 方法返回了一个 route,这是什么意思呢?

这个值被一个 ServiceProvider 注册了真正的路由类 ????

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。