首页 » Web前端 » phpioc容器技巧_一文详解IOC控制反转容器与依靠注入

phpioc容器技巧_一文详解IOC控制反转容器与依靠注入

访客 2024-11-22 0

扫一扫用手机浏览

文章目录 [+]

容器,字面上理解便是装东西的东西。
常见的变量、工具属性等都可以算是容器。
一个容器能够装什么,全部取决于你对该容器的定义。
当然,有这样一种容器,它存放的不是文本、数值,而是工具、工具的描述(类、接口)或者是供应工具的回调,通过这种容器,我们得以实现许多高等的功能,个中最常提到的,便是 “解耦” 、“依赖注入(DI)”。
本文就从这里开始。

本文一大半内容都是通过举例来让读者去理解什么是 IoC(掌握反转) 和 DI(依赖注入),通过理解这些观点,来更加深入。

phpioc容器技巧_一文详解IOC控制反转容器与依靠注入

IoC 容器出身的故事

讲解 IoC 容器有很多的文章,我之前也写过。
但现在我打算利用当下的灵感重新来过,那么开始吧。

phpioc容器技巧_一文详解IOC控制反转容器与依靠注入
(图片来自网络侵删)
超人和超能力,依赖的产生

面向工具编程,有以下几样东西无时不刻的打仗:接口、类还有工具。
这个中,接口是类的原型,一个类必须要遵守实在现的接口;工具则是一个类实例化后的产物,我们称其为一个实例。
当然这样说肯定不利于理解,我们就实际的写点中看不中用的代码赞助学习。

怪物横行的天下,总归须要点超级人物来摆平。

我们把一个“超人”作为一个类,

class Superman {}

我们可以想象,一个超人出身的时候肯定拥有至少一个超能力,这个超能力也可以抽象为一个工具,为这个工具定义一个描述他的类吧。
一个超能力肯定有多种属性、(操作)方法,这个尽情的想象,但是目前我们先大致定义一个只有属性的“超能力”,至于能干啥,我们往后再丰富:

class Power { / 能力值 / protected $ability; / 能力范围或间隔 / protected $range; public function __construct($ability, $range) { $this->ability = $ability; $this->range = $range; }}

这时候我们回过分,修正一下之前的“超人”类,让一个“超人”创建的时候被授予一个超能力:

class Superman{ protected $power; public function __construct() { $this->power = new Power(999, 100); }}

这样的话,当我们创建一个“超人”实例的时候,同时也创建了一个“超能力”的实例,但是,我们看到了一点,“超人”和“超能力”之间不可避免的产生了一个依赖。

所谓“依赖”,便是 “我若依赖你,我就不能离开你”。

在一个贯彻面向工具编程的项目中,这样的依赖随处可见。
少量的依赖并不会有太过直不雅观的影响,我们随着这个例子逐渐铺开,让大家逐步意识到,当依赖达到一个量级时,是若何一番噩梦般的体验。
当然,我也会自然而然的讲述如何办理问题。

一堆乱麻 —— 恐怖的依赖

之前的例子中,超能力类实例化后是一个详细的超能力,但是我们知道,超人的超能力是多元化的,每种超能力的方法、属性都有不小的差异,没法通过一种类描述完备。
我们现在进行修正,我们假设超人可以有以下多种超能力:

翱翔,属性有:翱翔速率、持续翱翔韶光蛮力,属性有:力量值能量弹,属性有:侵害值、射击间隔、同时射击个数

我们创建了如下类:

class Flight{ protected $speed; protected $holdtime; public function __construct($speed, $holdtime) { }}class Force{ protected $force; public function __construct($force) { }}class Shot{ protected $atk; protected $range; protected $limit; public function __construct($atk, $range, $limit) { }}

为了省事儿我没有详细写出 __construct() 这个布局函数的全部,只写了须要通报的参数。

好了,这下我们的超人有点“忙”了。
在超人初始化的时候,我们会根据须要来实例化其拥有的超能力吗,大致如下:

class Superman{ protected $power; public function __construct() { $this->power = new Fight(9, 100); // $this->power = new Force(45); // $this->power = new Shot(99, 50, 2); / $this->power = array( new Force(45), new Shot(99, 50, 2) ); / }}

我们须要自己手动的在布局函数内(或者其他方法里)实例化一系列须要的类,这样并不好。
可以想象,如果需求变更(不同的怪物横行地球),须要更多的有针对性的新的超能力,或者须要变更超能力的方法,我们必须 重新改造 超人。
换句话说便是,改变超能力的同时,我还得重新制造个超人。
效率太低了!
新超人还没创造完成天下早已被毁灭。

这时,灵机一动的人想到:为什么不可以这样呢?超人的能力可以被随时改换,只须要添加或者更新一个芯片或者其他装置啥的(想到钢铁侠没)。
这样的话就不要全体重新来过了。

对,便是这样的。

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

工厂模式,依赖转移!

当然,实现掌握反转的方法有几种。
在这之前,不如我们先理解一些好玩的东西。

我们可以想到,组件、工具(或者超人的模组),是一种可被生产的玩意儿,生产的地方当然是 “工厂(Factory)”,于是有人就提出了这样一种模式: 工厂模式。

工厂模式,顾名思义,便是一个类所依赖的外部事物的实例,都可以被一个或多个 “工厂” 创建的这样一种开拓模式,便是 “工厂模式”。

我们为了给超人制造超能力模组,我们创建了一个工厂,它可以制造各种各样的模组,且仅须要通过一个方法:

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]); } }}

这时候,超人 创建之初就可以利用这个工厂!

class Superman{ protected $power; public function __construct() { // 初始化工厂 $factory = new SuperModuleFactory; // 通过工厂供应的方法制造须要的模块 $this->power = $factory->makeModule('Fight', [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]) ); / }}

可以看得出,我们不再须要在超人初始化之初,去初始化许多第三方类,只需初始化一个工厂类,即可知足需求。
但这样彷佛和以前差异不大,只是没有那么多 new 关键字。
实在我们轻微改造一下这个类,你就明白,工厂类的真正意义和代价了。

class Superman{ protected $power; public function __construct(array $modules) { // 初始化工厂 $factory = new SuperModuleFactory; // 通过工厂供应的方法制造须要的模块 foreach ($modules as $moduleName => $moduleOptions) { $this->power[] = $factory->makeModule($moduleName, $moduleOptions); } }}// 创建超人$superman = new Superman([ 'Fight' => [9, 100], 'Shot' => [99, 50, 2]]);

现在修正的结果令人满意。
现在,“超人” 的创建不再依赖任何一个 “超能力” 的类,我们如若修正了或者增加了新的超能力,只须要针对修正 SuperModuleFactory 即可。
扩充超能力的同时不再须要重新编辑超人的类文件,使得我们变得很轻松。
但是,这才刚刚开始。

IoC 容器的主要组成 —— 依赖注入

由 “超人” 对 “超能力” 的依赖变成 “超人” 对 “超能力模组工厂” 的依赖后,对付小怪兽们变得更加得心应手。
但这也正如你所看到的,依赖并未解除,只是由原来对多个外部的依赖变成了对一个 “工厂” 的依赖。
如果工厂出了点麻烦,问题变得就很棘手。

实在大多数情形下,工厂模式已经足够了。
工厂模式的缺陷便是:接口未知(即没有一个很好的左券模型,关于这个我立时会有阐明)、产生工具类型单一。
总之便是,还是不足灵巧。
虽然如此,工厂模式依旧十分精良,并且适用于绝大多数情形。
不过我们为了讲解后面的依赖注入 ,这里就先浮夸一下工厂模式的毛病咯。

我们知道,超人依赖的模组,我们哀求有统一的接口,这样才能和超人身上的注入接口对接,终极起到提升超能力的效果。

事实上,我之前说谎了,不仅仅只有一堆小怪兽,还有更多的大怪兽。
嘿嘿。
额,这时候彷佛工厂的生产能力显得有些不敷 —— 由于工厂模式下,所有的模组都已经在工厂类中安排好了,如果有新的、高等的模组加入,我们必须修正工厂类(好比增加新的生产线):

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);}

上文中,我们定下了一个接口 (超能力模组的规范、左券),所有被创造的模组必须遵守该规范,才能被生产。

实在,这便是 php 中接口( interface )的用途和意义!
很多人以为,为什么 php 须要接口这种东西?难道不是 java 、 C# 之类的措辞才有的吗?这么说,只假如一个正常的面向工具编程措辞(虽然 php 可以面向过程),都该当具备这一特性。
由于一个 工具(object) 本身是由他的模板或者原型 —— 类 (class) ,经由实例化后产生的一个详细事物,而有时候,实现统一种方法且不同功能(或特性)的时候,会存在很多的类(class),这时候就须要有一个左券,让大家编写出可以被随时更换却不会产生影响的接口。
这种由编程措辞本身提出的硬性规范,会增加更多精良的特性。

虽然有些绕,但通过我们接下来的实例,大家会逐步领会接口带来的好处。

这时候,那些提出更好的超能力模组的高智贩子才,遵照这个接口,创建了下述(模组)类:

/ X-超能量 /class XPower implements SuperModuleInterface{ public function activate(array $target) { // 这只是个例子。

详细自行脑补 }}/ 终极炸弹 (就这么俗) /class UltraBomb implements SuperModuleInterface{ public function activate(array $target) { // 这只是个例子。

详细自行脑补 }}

同时,为了防止有些 “砖家” 自作聪明,或者一些叛徒恶意捣蛋,不遵守左券胡乱制造模组,影响超人,我们对超人初始化的方法进行改造:

class Superman{ protected $module; public function __construct(SuperModuleInterface $module) { $this->module = $module; }}

改造完毕!
现在,当我们初始化 “超人” 类的时候,供应的模组实例必须是一个 SuperModuleInterface 接口的实现。
否则就会提示缺点。

正是由于超人的创造变得随意马虎,一个超人也就不须要太多的超能力,我们可以创造多个超人,并分别注入须要的超能力模组即可。
这样的话,虽然一个超人只有一个超能力,但超人更随意马虎变多,我们也不怕怪兽啦!

现在有人迷惑了,你要讲的依赖注入呢?

实在,上面讲的内容,正是依赖注入。

什么叫做依赖注入?

本文从开头到现在提到的一系列依赖,只要不是由内部生产(比如初始化、布局函数 __construct 中通过工厂方法、自行手动 new 的),而是由外部以参数或其他形式注入的,都属于依赖注入(DI) 。
是不是豁然开朗?事实上,便是这么大略。
下面便是一个范例的依赖注入:

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

关于依赖注入这个本文的紧张配角,也就这么多须要讲的。
理解了依赖注入,我们就可以连续深入问题。
逐步走近本日的主角……

更为前辈的工厂 —— IoC 容器

刚刚列了一段代码:

$superModule = new XPower;$superMan = new Superman($superModule);

读者该当看出来了,手动的创建了一个超能力模组、手动的创建超人并注入了刚刚创建超能力模组。
呵呵,手动。

当代社会,该当是高效率的生产,干净的车间,完美的自动化妆配。

一群怪兽来了,如此低效率产出超人是不现实,我们须要自动化 —— 最多一条指令,千军万马来相见。
我们须要一种高等的生产车间,我们只须要向生产车间提交一个脚本,工厂便能够通过指令自动化生产。
这种更为高等的工厂,便是工厂模式的升华 —— IoC 容器。

class Container{ protected $binds; protected $instances; public function bind($abstract, $concrete) { if ($concrete instanceof Closure) { $this->binds[$abstract] = $concrete; } else { $this->instances[$abstract] = $concrete; } } public function make($abstract, $parameters = []) { if (isset($this->instances[$abstract])) { return $this->instances[$abstract]; } array_unshift($parameters, $this); return call_user_func_array($this->binds[$abstract], $parameters); }}

这时候,一个十分粗糙的容器就出身了。
现在的确很简陋,但不妨碍我们进一步提升他。
先着眼现在,看看这个容器如何利用吧!

// 创建一个容器(后面称作超级工厂)$container = new Container;// 向该 超级工厂添加超人的生产脚本$container->bind('superman', function($container, $moduleName) { return new Superman($container->make($moduleName));});// 向该 超级工厂添加超能力模组的生产脚本$container->bind('xpower', function($container) { return new XPower;});// 同上$container->bind('ultrabomb', function($container) { return new UltraBomb;});// 华标致的分割线 // 开始启动生产$superman_1 = $container->make('superman', 'xpower');$superman_2 = $container->make('superman', 'ultrabomb');$superman_3 = $container->make('superman', 'xpower');// ...随意添加

看到没?通过最初的 绑定(bind) 操作,我们向 超级工厂 注册了一些生产脚本,这些生产脚本在生产指令下达之时便会实行。
创造没有?我们彻底的解除了 超人 与 超能力模组 的依赖关系,更主要的是,容器类也丝毫没有和他们产生任何依赖!
我们通过注册、绑定的办法向容器中添加一段可以被实行的回调(可以是匿名函数、非匿名函数、类的方法)作为生产一个类的实例的 脚本 ,只有在真正的 生产(make) 操作被调用实行时,才会触发。

这样一种办法,使得我们更随意马虎在创建一个实例的同时办理其依赖关系,并且更加灵巧。
当有新的需求,只需其余绑定一个“生产脚本”即可。

实际上,真正的 IoC 容器更为高等。
我们现在的例子中,还是须要手动供应超人所须要的模组参数,但真正的 IoC 容器会根据类的依赖需求,自动在注册、绑定的一堆实例中征采符合的依赖需求,并自动注入到布局函数参数中去。
Laravel 框架的做事容器正是这么做的。
实现这种功能实在理论上并不麻烦,但我并不会在本文中写出,由于……我

不过我见告大家,这种自动征采依赖需求的功能,是通过反射(Reflection)实现的,恰好的,php 完美的支持反射机制!

现在,到目前为止,我们已经不再畏惧怪兽们了。
高智贩子才群策群力,井井有条,根据接口左券创造规范的超能力模组。
超人开始批量产出。
终极,大家都是超人,你也可以是哦!

Java程序员福利:金三银四,我把最近一年经历过的Java岗位口试,和一些刷过的口试题都做成了PDF,PDF都是可以免费分享给大家的,关注私信我:【101】,免费领取!
标签:

相关文章

今日头条算法如何实现个化推荐与精准传播

信息传播方式发生了翻天覆地的变化。今日头条作为国内领先的信息分发平台,凭借其强大的算法推荐系统,吸引了海量用户。今日头条的算法究竟...

Web前端 2025-01-31 阅读1 评论0

今日头条算法关闭之谜内容分发新格局

今日头条作为一款备受瞩目的新闻资讯平台,凭借其独特的算法推荐机制,吸引了大量用户。近期有关今日头条算法关闭的消息引发了广泛关注。本...

Web前端 2025-01-31 阅读1 评论0

今日头条算法智能推荐背后的科技魅力

信息爆炸的时代已经到来。人们每天在互联网上接触到海量的信息,如何从中筛选出有价值的内容,成为了人们关注的焦点。今日头条作为一款智能...

Web前端 2025-01-31 阅读1 评论0

今日头条算法专利申请个化推荐的秘密武器

信息爆炸的时代已经来临。在众多信息中,如何快速找到自己感兴趣的内容成为了一个难题。今日头条作为中国领先的资讯平台,凭借其独特的算法...

Web前端 2025-01-31 阅读1 评论0

今日头条算法机器推荐模式的秘密与挑战

大数据、人工智能等新兴技术的应用已经渗透到我们生活的方方面面。在信息爆炸的时代,人们获取信息的渠道越来越丰富,如何在海量信息中找到...

Web前端 2025-01-31 阅读1 评论0