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框架总结(四) -- 服务容器的更多相关文章

  1. 简单理解laravel框架中的服务容器,服务提供者以及怎样调用服务

      laravel被称为最优雅的框架,最近正在学习中,对于用惯了thinkphp.ci框架的人来说,服务容器.服务提供者,依赖注入这些概念简直是一脸懵逼.我花了些时间梳理了一下,也不敢确定自己说的是对 ...

  2. Laravel源码解析 — 服务容器

    前言 本文对将系统的对 Laravel 框架知识点进行总结,如果错误的还望指出 阅读书籍 <Laravel框架关键技术解析> 陈昊 学习课程 Laravel5.4快速开发简书网站 轩脉刃 ...

  3. Minor【 PHP框架】4.服务容器与服务提供者

    框架Github地址:github.com/Orlion/Minor (如果觉得还不错给个star哦(^-^)V) 框架作者: Orlion 知乎:https://www.zhihu.com/peop ...

  4. Laravel之Service Container服务容器

    managing class dependencies and performing dependency injection. Dependency injection is a fancy phr ...

  5. [php]laravel框架容器管理的一些要点

    本文面向php语言的laravel框架的用户,介绍一些laravel框架里面容器管理方面的使用要点.文章很长,但是内容应该很有用,希望有需要的朋友能看到.php经验有限,不到位的地方,欢迎帮忙指正. ...

  6. laravel框架容器管理

    来自http://www.cnblogs.com/chy1000/p/7072936.html 本文面向php语言的laravel框架的用户,介绍一些laravel框架里面容器管理方面的使用要点.文章 ...

  7. laravel框架容器管理的一些要点

    本文面向php语言的laravel框架的用户,介绍一些laravel框架里面容器管理方面的使用要点.文章很长,但是内容应该很有用,希望有需要的朋友能看到.php经验有限,不到位的地方,欢迎帮忙指正. ...

  8. laravel框架容器管理的一些要点(转)

    本文面向php语言的laravel框架的用户,介绍一些laravel框架里面容器管理方面的使用要点.文章很长,但是内容应该很有用,希望有需要的朋友能看到.php经验有限,不到位的地方,欢迎帮忙指正. ...

  9. Ioc容器与laravel服务容器初探

    一.Ioc容器 某天,小J心血来潮,决定建造一艘星舰,这艘星舰要搭载"与众不同最时尚,开火肯定棒"的电磁炮.于是他写了一个星舰类: class ElectromagneticGun ...

随机推荐

  1. Trie树

    一.什么是trie树 1.Trie树 (特例结构树)   Trie树,又称单词查找树.字典树,是一种树形结构,是一种哈希树的变种,是一种用于快速检索的多叉树结构.典型应用是用于统计和排序大量的字符串( ...

  2. EL表达式Expression Language

    表达式语言Expression Language目的:简化jsp代码 EL内置对象 1.pageContext2.pageScope3.requestScope4.sessionScope5.appl ...

  3. OAuth2.0 微博登陆网站功能的实现(一)获取用户授权及令牌 Access Token

    在登陆一些网站的时候,可以选择登陆方式为第三方登陆,例如微博登陆,以爱奇艺为例,进入首页,点击 ”登陆“,会弹出登录框: 除了本站登陆外,还可以选择其他第三方登陆,比如微博登陆.QQ 登陆.微信登陆等 ...

  4. 新安装个Myeclipse,导入以前做的程序后程序里好多错,提示The import java.util cannot be resolved

    原因:这是由于你的项目buildpath不对原来的项目,比如采用了原先的MyEclipse自带的jdk (D:\myeclipse\XXXXXX)结果,你现在换了一个,原来的没了就导致了现在这种错误, ...

  5. maketrans translate

    1. makestrans()用法 语法: str.maketrans(intab, outtab]); Python maketrans() 方法用于创建字符映射的转换表,对于接受两个参数的最简单的 ...

  6. yii1 render方法解析(记录下)

    先判断主题(themes)中是否有相对应的文件,如果没有变换file为protected/views路径下的文件,如果有文件则变换为themes路径下的文件.然后,如果加载了viewrender模块( ...

  7. What is the difference between parameterized queries and prepared statements?

    Both parameterized queries and prepared statements are exactly the same thing. Prepared statement se ...

  8. iOS cocospods Updating local specs repositories

    pod install --verbose --no-repo-update (在安装的时候) pod update --verbose --no-repo-update (在更新库的时候) 如果长时 ...

  9. C++ 中的类构造函数 & 析构函数

    类的构造函数 类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行. 构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void.构造函数可用于为某些成员变量设置 ...

  10. lua OOP实现对象的链式调用

    数学中的链式法则 http://sx.zxxk.com/ArticleInfo.aspx?InfoID=164649 链式微分法则:实数运算的链式法则:对数运算的链式法则:平行公理的链式法则:向量运算 ...