深入 Laravel 内核之IOC容器
升级工厂前的准备工作
无规矩不成方圆,随着越来越多的行为出现,我们需要需要定下一些规范。
为了约束每一个行为的规范,需要定义一个行为接口:
interface BehaviorInterface
{
/**
* 行为激活方法
* @param array $values 激活的参数
*/
public function activate(array $values);
}
按照接口规范修改前述的行为类:
class Attack implements BehaviorInterface{
protected $value = 0;
public function __construct($value)
{
$this->value = $value;
}
public function activate(array $values){}
}
class Defend implements BehaviorInterface{
protected $value = 0;
public function __construct($value){}
public function activate(array $values){}
}
class Move implements BehaviorInterface{
protected $speed;
public function __construct($speed){}
public function activate(array $values){}
}
class Skill1 implements BehaviorInterface{
protected $name = '暴击';
public function __construct(){}
public function activate(array $values){}
}
class Skill2 implements BehaviorInterface{
protected $name = '眩晕';
public function __construct(){}
public function activate(array $values){}
}
使用依赖注入升级工厂
- 为了解决工厂对行为的依赖,在每一种行为定义好之后就放到工厂中(依赖注入)。
- 放到工厂中的行为必须是依照规范定义的。
class BehaviorFactory
{
protected $instances;
public function setBehavior($behaviorName, $behavior)
{
if($behavior instanceof BehaviorInterface) {
$this->instances[$behaviorName] = $behavior;
}
}
public function getBehavior($behaviorName)
{
return $this->instances[$behaviorName];
}
}
$factory = new BehaviorFactory();
$factory->setBehavior('Attack', new Attack(0));
$factory->setBehavior('Defend', new Defend(0));
$factory->setBehavior('Move', new Move(0));
$factory->setBehavior('Skill1', new Skill1());
$factory->setBehavior('Skill2', new Skill2());
对英雄类做一些简单的修改:
class Hero
{
protected $behavior = [];
public function __construct($factory, array $behaviors)
{
// 通过工厂提供的方法制造需要的模块
foreach ($behaviors as $behaviorName => $behaviorOptions) {
$behavior = $factory->getBehavior($behaviorName);
$this->behavior[] = $behavior->activate($behaviorOptions);
}
}
}
$hero = new Hero($factory, [
'Attack' => [10],
'Defend' => [5],
'Move' => [30],
'Skill1' => [],
]);
至此,我们通过依赖注入,已经彻底地解决了英雄对行为的依赖、英雄对工厂的依赖、工厂对行为的依赖。
但是还有一些不足之处:
- 所有的对象,我们都必须手动去实例化;
- 这些行为,无论英雄是否需要,都要提前实例化并放到工厂中。
再谈依赖注入
前面的文章中我们已经讲解了如何通过PHP的反射机制来解决依赖注入的问题。这里我们再来深入一下对依赖注入的理解。
什么是依赖注入:
所谓的依赖注入,意思是只要不是在类内部产生的对象依赖(比如在初始化、构造函数中,通过工厂方法或者手动实例化),而是由外部以参数或其他形式注入(传入)的,都属于依赖注入(DI)。
超级工厂-IOC服务容器
接下来将我们的升级工厂,进一步改造为超级工厂。
定义一个简单的容器类,用来存储对象实例化的方式和创建对象:
class Container
{
// 存放抽象类与对象实例化方式关系的数据
protected $binds;
// 存放抽象类与已有的对象关系的数据
protected $instances;
// 将抽象类与[对象实例化方式|已有的对象]分别存入数组
public function bind($abstract, $concrete)
{
// 如果第二个参数是闭包,则存入 $binds 数组
if ($concrete instanceof Closure) {
$this->binds[$abstract] = $concrete;
}
// 如果第二个参数不是闭包,则为已经实例化的对象,存入 $instances 数组
else {
$this->instances[$abstract] = $concrete;
}
}
// 根据对象实例化的方式创建对象并返回
public function make($abstract, $parameters = [])
{
// 如果是已经实例化的对象则直接返回
if (isset($this->instances[$abstract])) {
return $this->instances[$abstract];
}
// 将$this(容器)放入参数数组的头部
// [$this, $param1, $param2...]
array_unshift($parameters, $this);
// 调用对象实例化的方法(即绑定时传入的闭包函数和当前的参数),得到实例化后的对象并返回
// function($container, $behaviorName) { return new Hero($behavior); }
// function($this, 'skill1') { return new Hero('skill1'); }
return call_user_func_array($this->binds[$abstract], $parameters);
}
}
测试这个容器:
// 创建一个容器
$container = new Container;
// 向该容器添加英雄的生产方式(实例化的方式)
$container->bind('hero', function($container, $behaviorName) {
$behavior = $container->make($behaviorName);
return new Hero($behavior);
});
// 向该容器添加行为的生产方式(实例化的方式)
$container->bind('skill1', function($container) {
return new Skill1;
});
// 同上
$container->bind('skill2', function($container) {
return new Skill2;
});
// 开始启动生产
$hero1 = $container->make('hero', ['skill1']);
$hero2 = $container->make('hero', ['skill2']);
在这个容器中,我们通过绑定操作,可以向超级工厂注册很多各种各样的生产脚本(可以是匿名函数、非匿名函数、类的方法),这些脚本在生产操作触发的时候就会被容器调用执行。
通过依赖注入和容器,我们彻底解决了所有对象之间的依赖关系;最重要的是,容器类也没有和英雄、行为之间有任何的依赖。另外也不需要再去手动实例化对象,还实现了按需实例化。
实际上,真正的 IoC 容器更为高级,会根据类的依赖需求,使用PHP的反射(Reflection)机制,自动在注册、绑定的一堆实例中搜寻符合的相关依赖,并自动注入到构造函数参数中去。
深入 Laravel 内核之IOC容器的更多相关文章
- 理解PHP 依赖注入|Laravel IoC容器
看Laravel的IoC容器文档只是介绍实例,但是没有说原理,之前用MVC框架都没有在意这个概念,无意中在phalcon的文档中看到这个详细的介绍,感觉豁然开朗,复制粘贴过来,主要是好久没有写东西了, ...
- Ioc容器与laravel服务容器初探
一.Ioc容器 某天,小J心血来潮,决定建造一艘星舰,这艘星舰要搭载"与众不同最时尚,开火肯定棒"的电磁炮.于是他写了一个星舰类: class ElectromagneticGun ...
- laravel核心Ioc容器
laravel容器和依赖注入 啥是Ioc容器,方便我们实现依赖注入的一种实现,也就是说依赖注入不一定需要控制反转容器,只不过使用容器可能会方便些. laravel通过向容器中绑定接口的具体实现,可实现 ...
- 通过中看不中用的代码分析Ioc容器,依赖注入....
/** * 通过生产拥有超能力的超人实例 来理解IOC容器 */ //超能力模组接口 interface SuperModuleInterface{ public function activate( ...
- 【转】Spring学习---Spring IoC容器的核心原理
[原文] Spring的两个核心概念:IoC和AOP的雏形,Spring的历史变迁和如今的生态帝国. IoC和DI的基本概念 IoC(控制反转,英文含义:Inverse of Control)是Spr ...
- Laravel核心之IOC和Facade 架构分析1
控制反转(Inversion of Control) 缩写为IoC 最常见的方式叫做依赖注入 简单说来,就是一个类把自己的的控制权交给另外一个对象,类间的依赖由这个对象去解决. Laravel 中的使 ...
- PHP 在Swoole中使用双IoC容器实现无污染的依赖注入
简介: 容器(container)技术(可以理解为全局的工厂方法), 已经是现代项目的标配. 基于容器, 可以进一步实现控制反转, 依赖注入. Laravel 的巨大成功就是构建在它非常强大的IoC容 ...
- laravel5.8 IoC 容器
网上 对容器的解释有很多,这里只是记录,搬运! 1.简单理解: 2019-10-10 11:24:09 解析 lavarel 容器 IoC 容器 作用 就是 “解耦” .“依赖注入(DI) IoC 容 ...
- Spring学习记录1——IoC容器
IoC容器 1.1 IoC概述 Ioc(Inverse of Control,控制反转)是Spring容器的内核.对于软件来说,即某一接口具体实现类的选择控制权从调用类中移除,转交给第三方决定,即由 ...
随机推荐
- SpringBoot环境下java实现文件的下载
思路:文件下载,就是给服务器上的文件创建输入流,客户端创建输出流,将文件读出,读入到客户端的输出流中,(流与流的转换) package com.cst.icode.controller; import ...
- 【Python】文本包jieba使用
看了一个教程:https://www.cnblogs.com/wkfvawl/p/9487165.html 有些不懂的地方自己查阅了一下 键值的添加,获得文件中相同字符出现的次数, counts = ...
- Java高精度基础+开根
在焦作站的acm网络赛中遇到了一个高精度开根的水题--但是那时候WA了 后面学写java补题还T了orz 所以写一篇文章来记录一下java的大整数类型的基础和开根还有一点心得体会吧 首先给那一题的题面 ...
- 利用 clip-path 实现动态区域裁剪
背景 今天逛 CodePen,看到了这样一个非常有意思的效果: CodePen Demo -- Material Design Menu By Bennett Feely 这个效果还是有一些值得探讨学 ...
- malloc实现
任何一个用过或学过C的人对malloc都不会陌生.大家都知道malloc可以分配一段连续的内存空间,并且在不再使用时可以通过free释放 掉.但是,许多程序员对malloc背后的事情并不熟悉,许多人甚 ...
- [BUUCTF]PWN——bjdctf_2020_babyrop2
bjdctf_2020_babyrop2 附件 步骤: 例行检查,64位程序,开启了NX和canary保护 2. 试运行一下程序,看看大概的情况 提示我们去泄露libc 3. 64位ida载入,从ma ...
- w4sp-lab安装
扯淡 i春秋有个答题活动,苟了个奖品,我选了一本书:<wireshark与metasploit实战指南>,里面有个配套环境,本来看着书上说使用docker搭建的,以为很简单,只需要pull ...
- 如何在 GitHub 上高效阅读源码?
原文链接: 如何在 GitHub 上高效阅读源码? 之前听说过一个故事,一个领导为了提高团队战斗力,把团队成员集中起来,搞封闭开发,重点还是在没有网的条件下. 结果就是一个月过去了,产出基本为零. 我 ...
- 面试官:HashSet如何保证元素不重复?
本文已收录<Java常见面试题>系列,Git 开源地址:https://gitee.com/mydb/interview HashSet 实现了 Set 接口,由哈希表(实际是 HashM ...
- CF1077A Frog Jumping 题解
Content 在一个数轴上有一个动点,初始时在 \(0\) 这个位置上,接下来有若干次操作,对于第 \(i\) 次操作: 如果 \(i\) 是奇数,那么动点往右移 \(a\) 个单位. 如果 \(i ...