zoukankan      html  css  js  c++  java
  • 服务容器

       
    服务器容器就是一个普通的容器,用来装类的实例,然后在需要的时候再取出来
    
    用更专业的术语来说就是服务容器实现控制反转(IoC)
    
    意思是正常的情况下类A需要一个类B的时候,我们需要自己去new类B。意味着我们必须知道类B的更多细节。
    
    比如构造函数,随着项目的复杂性增大,这种依赖是毁灭性的。控制反转的意思是,将A主动获取类B的过程颠倒过来变成被动的。
    
    类A只需要声明它需要什么,然后由容器提供。
    
    这样做的好处是,类A不依赖B的实现,这样在一定程度上解决了耦合问题。
    
    在laravel的服务器中,为了实现控制反转,1.依赖注入2.绑定

    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('NamespaceToYourInterfacePay', 'NamespaceToYourClassWeixin');

    //解析方式:服务容器(Service Container)

    //如果你需要注入服务的对象是通过服务容器(Service Container)来解析的,才可以使用类型提示(Type Hint)来进行自动解析并注入服务。

    第三,上下文绑定

    像上边这样,通过绑定接口到实例,只能自动解析为一个实例,也就是你绑定的实例;如果你想要的结果是,不同的类,构造器通过类型提示(Type Hint)相同的接口,注入不同的实例,可以像下边这样(上下文绑定,Context Binding):

    $this->app->when('NamespaceToYourClassA') ->needs('NamespaceToYourInterfacePay') ->give('NamespaceToYourClassWeixin'); 

    $this->app->when('NamespaceToYourClassB') ->needs('NamespaceToYourInterfacePay') ->give('NamespaceToYourClassAli');

    通过上下文绑定,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
        }
    }
  • 相关阅读:
    计算机术语
    【转】 物理内存和线性空间
    windows Visual Studio 上安装 CUDA【转载】
    windows Notepad++ 上配置 vs 编译器 , 编译并运行
    单列模式 [转载]
    Java Swing布局管理器GridBagLayout的使用示例 [转]
    五年java工作应具备的技能
    三年java软件工程师应有的技技能
    京东面试题 Java相关
    京东笔试题总结
  • 原文地址:https://www.cnblogs.com/yeyublog/p/6909918.html
Copyright © 2011-2022 走看看