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
    }
}
时间: 2024-08-08 14:43:18

laravel框架总结(四) -- 服务容器的相关文章

Laravel框架学习(四)

一. composer的安装: 1.Composer是什么? 是 PHP 用来管理依赖(dependency)关系的工具. 你可以在自己的项目中声明所依赖的外部工具库(libraries), Composer 会帮你安装这些依赖的库文件. 2.网址:https://getcomposer.org 下载:https://getcomposer.org/download/ 中国全量镜像:http://pkg.phpcomposer.com/ 启用本镜像服务命令: composer config -g

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

4.1 服务提供者 关于服务容器可以参考我的另外一篇文章:http://www.cnblogs.com/orlion/p/4797422.html Minor使用IoC(Inversion of Control,控制倒转,这是一个设计模式,可以先查看下百科)容器这个强有力的工具管理类依赖.依赖注入(也是一种设计模式,一般用于实现IoC)是 一个不用编写固定代码来处理类之间依赖的方法,相反的,这些依赖是在运行时注入的,这样允许处理依赖时具有更大的灵活性. 服务提供者是服务容器中的单元,是一个普通的

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

本文面向php语言的laravel框架的用户,介绍一些laravel框架里面容器管理方面的使用要点.文章很长,但是内容应该很有用,希望有需要的朋友能看到.php经验有限,不到位的地方,欢迎帮忙指正. 1. laravel容器基本认识 laravel框架是有一个容器框架,框架应用程序的实例就是一个超大的容器,这个实例在bootstrap/app.php内进行初始化: 这个文件在每一次请求到达laravel框架都会执行,所创建的$app即是laravel框架的应用程序实例,它在整个请求生命周期都是唯

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

本文面向php语言的laravel框架的用户,介绍一些laravel框架里面容器管理方面的使用要点.文章很长,但是内容应该很有用,希望有需要的朋友能看到.php经验有限,不到位的地方,欢迎帮忙指正. 1. laravel容器基本认识 laravel框架是有一个容器框架,框架应用程序的实例就是一个超大的容器,这个实例在bootstrap/app.php内进行初始化: 这个文件在每一次请求到达laravel框架都会执行,所创建的$app即是laravel框架的应用程序实例,它在整个请求生命周期都是唯

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

本文面向php语言的laravel框架的用户,介绍一些laravel框架里面容器管理方面的使用要点.文章很长,但是内容应该很有用,希望有需要的朋友能看到.php经验有限,不到位的地方,欢迎帮忙指正. 1. laravel容器基本认识 laravel框架是有一个容器框架,框架应用程序的实例就是一个超大的容器,这个实例在bootstrap/app.php内进行初始化: 这个文件在每一次请求到达laravel框架都会执行,所创建的$app即是laravel框架的应用程序实例,它在整个请求生命周期都是唯

Laravel框架怎样使用阿里云ACE缓存服务

Laravel框架怎样使用阿里云ACE缓存服务 之前我写了一篇在 Laravel 4 框架中使用阿里云 OCS 缓存的文章.介绍了怎样通过扩展 Laravel 4 来支持须要 SASL 认证的阿里云 OCS 缓存服务.有网友问我.ACE 的缓存怎么在 Laravel 4 中使用.我本来认为应该能够全然用同样的办法,后来自己尝试的时候才发现,ACE 的缓存区别很大.所以再写一篇,介绍一下怎样在 Laravel 框架中使用阿里云 ACE 的缓存服务. 怎样扩展 Laravel 的缓存驱动 在 Lar

Laravel 服务容器 IoC(控制反转) 和 DI(依赖注入)

Laravel 服务容器 IoC(控制反转) 和 DI(依赖注入) IoC 容器, laravel 的核心 Laravel 的核心就是一个 IoC 容器,根据文档,称其为“服务容器”,顾名思义,该容器提供了整个框架中需要的一系列服务.作为初学者,很多人会在这一个概念上犯难,因此,我打算从一些基础的内容开始讲解,通过理解面向对象开发中依赖的产生和解决方法,来逐渐揭开“依赖注入”的面纱,逐渐理解这一神奇的设计理念. 本文一大半内容都是通过举例来让读者去理解什么是 IoC(控制反转) 和 DI(依赖注

Laravel框架如何使用阿里云ACE缓存服务

Laravel框架如何使用阿里云ACE缓存服务 之前我写了一篇在 Laravel 4 框架中使用阿里云 OCS 缓存的文章,介绍了如何通过扩展 Laravel 4 来支持需要 SASL 认证的阿里云 OCS 缓存服务.有网友问我,ACE 的缓存怎么在 Laravel 4 中使用.我本来觉得应该可以完全用相同的办法,后来自己尝试的时候才发现,ACE 的缓存差别非常大.所以再写一篇,介绍一下如何在 Laravel 框架中使用阿里云 ACE 的缓存服务. 如何扩展 Laravel 的缓存驱动 在 La

laravel 学习笔记 —— 神奇的服务容器

转载自:https://www.insp.top/learn-laravel-container 容器,字面上理解就是装东西的东西.常见的变量.对象属性等都可以算是容器.一个容器能够装什么,全部取决于你对该容器的定义.当然,有这样一种容器,它存放的不是文本.数值,而是对象.对象的描述(类.接口)或者是提供对象的回调,通过这种容器,我们得以实现许多高级的功能,其中最常提到的,就是 “解耦” .“依赖注入(DI)”.本文就从这里开始. IoC 容器, laravel 的核心 Laravel 的核心就