laravel框架总结(四) -- 服务容器
1.依赖
我们定义两个类:class Supperman 和 class Power,现在我们要使用Supperman ,而Supperman 依赖了Power
class Supperman {
private $power;
public function __construct(){
$this->power = new Power;
}
}
一旦Power发生了变化,Supperman 不得不修改,这种就叫耦合程度太高,所以面临的问题是解耦,就需要用到控制反转.
2.依赖注入
只要不是由内部生产(比如初始化、构造函数 __construct 中通过工厂方法、自行手动 new 的),而是由外部以参数或其他形式注入的,都属于 依赖注入(DI)
方式1、用一个方法把依赖类引入进来。俗称setter方法。
class Supperman{
public $newPower;
public function setNew($Power){
$this->newPower=$Power;
}
public function show(){
$this->newPower->use();
}
}
方式2、用构造器引入依赖类。
class Supperman{
public $newPower;
public function __construct($Power){
$this->newPower=$Power;
}
public function show(){
$this->newPower->use();
}
}
方式3、直接设置属性
class Supperman{
public $newPower=new Power();
public function show(){
$this->newPower->use();
}
}
3.下面我们具体来分析
IoC 容器诞生的故事——石器时代(原始模式)
#超人类
class Superman {
protected $power;
public function __construct() {
$this->power = new Power(999, 100);
}
}
#超能力制造类
class Power {
protected $ability;//能力值
protected $range; //能力范围
public function __construct($ability, $range) {
$this->ability = $ability; $this->range = $range;
}
}
“超人”和“超能力”之间不可避免的产生了一个依赖。
其实超人是有多种超能力:我们将power细分为flight,force,shot
我们创建了如下类:
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) {
}
}
现在我们要实例化一个超人就需要把各种能力都传给超人,这些都要超人自己做
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);
}
}
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]);
}
}
}
有了这个工厂,直接调用工厂里面的方法,超人就不用自己去实例化超能力了
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]);
}
}
不过这样和以前好像差不多,只是没有那么多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] ]);
现在我们可以在超人类外面自由控制生产出何种能力的超人了,我们再也不用关心超人类中需要提前写入需要何种能力了(以前超人类中需要自己new 超能力或者$factory->make超能力),这就是控制反转
我们不应该手动在 “超人” 类中固化了他的 “超能力” 初始化的行为,而转由外部负责,由外部创造超能力模组、装置或者芯片等(我们后面统一称为 “模组”),植入超人体内的某一个接口,这个接口是一个既定的,只要这个 “模组” 满足这个接口的装置都可以被超人所利用,可以提升、增加超人的某一种能力。这种由外部负责其依赖需求的行为,我们可以称其为 “控制反转(IoC)”。
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 (依赖注入)
为了约束超能力,我们制定了超能力的规范,接口SuperModuleInterface,
interface SuperModuleInterface {
public function activate(array $target);
}
各种类型的超能力继承超能力接口
//超能力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
}
}
我们现在要制造一个超人
// 超能力模组
$superModule = new XPower;
// 初始化一个超人,并注入一个超能力模组依赖
$superMan = new Superman($superModule);
IoC 容器诞生的故事——科技时代(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) 操作被调用执行时,才会触发。
这样一种方式,使得我们更容易在创建一个实例的同时解决其依赖关系,并且更加灵活。当有新的需求,只需另外绑定一个“生产脚本”即可。
在laravel框架中如何绑定和解析
几乎所有的服务容器绑定都会注册至服务提供者(下一篇总结中会介绍)中
第一,通过绑定服务ID的方式定义的服务,只能通过服务ID来获取
singleton 方法绑定一个只会被解析一次的类或接口至容器中,且后面的调用都会从容器中返回相同的
// 绑定方式:定义服务
//实例:
$this->app->singleton('service_id', function () {
return new Service();
})
// 获取服务,以下方式等价
$this->app->make('service_id'); $this->app['service_id'];
第二,你想通过接口的类型提示(Type Hint)来自动解析并注入服务
需要像下边这样:bind 方法注册一个绑定,传递我们希望注册的类或接口名称,并连同返回该类实例的闭包:
// 绑定方式:绑定接口到实例
$this->app->bind('Namespace\To\Your\Interface\Pay', 'Namespace\To\Your\Class\Weixin');
//解析方式:服务容器(Service Container)
//如果你需要注入服务的对象是通过服务容器(Service Container)来解析的,才可以使用类型提示(Type Hint)来进行自动解析并注入服务。
第三,上下文绑定
像上边这样,通过绑定接口到实例,只能自动解析为一个实例,也就是你绑定的实例;如果你想要的结果是,不同的类,构造器通过类型提示(Type Hint)相同的接口,注入不同的实例,可以像下边这样(上下文绑定,Context Binding):
$this->app->when('Namespace\To\Your\Class\A') ->needs('Namespace\To\Your\Interface\Pay') ->give('Namespace\To\Your\Class\Weixin');
$this->app->when('Namespace\To\Your\Class\B') ->needs('Namespace\To\Your\Interface\Pay') ->give('Namespace\To\Your\Class\Ali');
通过上下文绑定,A的实例会注入Weixin的实例,而B的实例会注入Ali的实例。像下边:
class A {
public functions __construct(Pay $pay) {
var_dump($pay instanceof Weixin); // True
}
}
class B {
public functions __construct(Pay $pay) {
var_dump($pay instanceof Ali); // True
}
}
本文超人的例子整理自: https://www.insp.top/article/learn-laravel-container
laravel框架总结(四) -- 服务容器的更多相关文章
- 简单理解laravel框架中的服务容器,服务提供者以及怎样调用服务
laravel被称为最优雅的框架,最近正在学习中,对于用惯了thinkphp.ci框架的人来说,服务容器.服务提供者,依赖注入这些概念简直是一脸懵逼.我花了些时间梳理了一下,也不敢确定自己说的是对 ...
- Laravel源码解析 — 服务容器
前言 本文对将系统的对 Laravel 框架知识点进行总结,如果错误的还望指出 阅读书籍 <Laravel框架关键技术解析> 陈昊 学习课程 Laravel5.4快速开发简书网站 轩脉刃 ...
- Minor【 PHP框架】4.服务容器与服务提供者
框架Github地址:github.com/Orlion/Minor (如果觉得还不错给个star哦(^-^)V) 框架作者: Orlion 知乎:https://www.zhihu.com/peop ...
- Laravel之Service Container服务容器
managing class dependencies and performing dependency injection. Dependency injection is a fancy phr ...
- [php]laravel框架容器管理的一些要点
本文面向php语言的laravel框架的用户,介绍一些laravel框架里面容器管理方面的使用要点.文章很长,但是内容应该很有用,希望有需要的朋友能看到.php经验有限,不到位的地方,欢迎帮忙指正. ...
- laravel框架容器管理
来自http://www.cnblogs.com/chy1000/p/7072936.html 本文面向php语言的laravel框架的用户,介绍一些laravel框架里面容器管理方面的使用要点.文章 ...
- laravel框架容器管理的一些要点
本文面向php语言的laravel框架的用户,介绍一些laravel框架里面容器管理方面的使用要点.文章很长,但是内容应该很有用,希望有需要的朋友能看到.php经验有限,不到位的地方,欢迎帮忙指正. ...
- laravel框架容器管理的一些要点(转)
本文面向php语言的laravel框架的用户,介绍一些laravel框架里面容器管理方面的使用要点.文章很长,但是内容应该很有用,希望有需要的朋友能看到.php经验有限,不到位的地方,欢迎帮忙指正. ...
- Ioc容器与laravel服务容器初探
一.Ioc容器 某天,小J心血来潮,决定建造一艘星舰,这艘星舰要搭载"与众不同最时尚,开火肯定棒"的电磁炮.于是他写了一个星舰类: class ElectromagneticGun ...
随机推荐
- zju(7)ADC操作实验
1.实验目的 1.学习和掌握S3C2410下ADC接口的操作方法以及应用程序的编写: 二.实验内容 1.编写EduKit-IV实验箱Linux操作系统下按键ADC的应用程序,并显示ADC的值. 三.主 ...
- zju(3)内核编译与运行
1.实验目的 学习和掌握Linux配置和编译的基本步骤. 二.实验内容 1. 对Linux内核及用户程序进行配置: 2. 编译生成内核映像文件: 3. 把编译的映像文件烧写到FLASH中,查看运行结果 ...
- A+B Problem III-(涉及误差)NYOJ-477
描述求A+B是否与C相等. 输入 T组测试数据. 每组数据中有三个实数A,B,C(-10000.0<=A,B<=10000.0,-20000.0<=C<=20000.0) ...
- php预定义变量,超全局变量,魔术方法,特殊函数变量使用
<?php /* * 本代码全部为测试函数代码,部分注释和写实例 * * 修改php.ini variables_order=”EGPCS” * 请注意$_REQUEST在优先级传参的时候会造成 ...
- 请求量限制方法-使用本地Cache记录当前请求量[坑]
有个需求:需要限制每个账户请求服务器的次数(该次数可以配置在DB,xml文件或其他).单位:X次/分钟.若1分钟内次数<=X 则允许访问,1分钟内次数>X则不再允许访问. 这类需求很常 ...
- IAR EW8051-8.10.4安装及破解方法
第一步:获取破解license 1: 点击桌面左下角“开始”按钮,找到cmd.exe,右键创建cmd.exe 快捷方式到桌面: ————如果是windows7 ,请右键点击cmd.exe 快捷图标,点 ...
- android:layout_gravity 和 android:gravity 的区别
gravity 这个英文单词是重心的意思,在这里就表示停靠位置的意思. android:layout_gravity 和 android:gravity 的区别 从名字上可以看到,android:gr ...
- 基于VirtualBox的多重载入
问题描述 这个问题要追溯到中秋之前,也就是写第一周博客的时候,当时我用的还是虚拟机上的ubuntu:当时我的ubuntu不是最新版,所以有提示升级,你懂的,我升了(因为时间有点久,我先去吃了个饭):等 ...
- UINavigation Bar中使用UIcollectionView,在UIcollectionView的顶端和低端出现空白的问题
在含有UINavigation Bar的视图中,使用UIcollectionView,会在UIcollectionView的顶端和低端出现空白,需要添加下面 一句代码,空白即可消失 //如果不加这 ...
- kibana使用操作部分
1.kibana的概念及特点. 概念:数据可视化平台工具 特点: - 灵活的分析和可视化平台 - 实时总结和流数据的图表 - 为不同的用户显示直观的界面 - 即时分享和嵌入的仪表板 2.kib ...