zoukankan      html  css  js  c++  java
  • YII框架的依赖注入容器与服务定位器简述

    依赖注入容器

    依赖注入(Dependency Injection,DI)容器就是一个对象use yiidiContainer,它知道怎样初始化并配置对象及其依赖的所有对象。

    依赖注入和服务定位器都是流行的设计模式,它们使你可以用充分解耦且更利于测试的风格构建软件。

    构造方法注入

    class Foo
    {
        public function __construct(Bar $bar)
        {
        }
    }
    
    $container = new Container();
    $foo = $container->get('Foo');//get里面是类名注意使用命名空间
    // 上面的代码等价于:
    $bar = new Bar;
    $foo = new Foo($bar);
    

    Setter 和属性注入

    use yiiaseObject;
    
    class Foo extends Object
    {
        public $bar;
    
        private $_qux;
    
        public function getQux()
        {
            return $this->_qux;
        }
    
        public function setQux(Qux $qux)
        {
            $this->_qux = $qux;
        }
    }
    
    $container->get('Foo', [], [
        'bar' => $container->get('Bar'),
        'qux' => $container->get('Qux'),
    ]);
    

    PHP 回调注入

    $container->set('Foo', function () {
        return new Foo(new Bar);
    });
    
    $foo = $container->get('Foo');
    

    注册依赖关系

    可以用 yiidiContainer::set() 注册依赖关系。注册会用到一个依赖关系名称和一个依赖关系的定义。依赖关系名称可以是一个类名,一个接口名或一个别名。依赖关系的定义可以是一个类名,一个配置数组,或者一个 PHP 回调。

    $container = new yiidiContainer;
    
    // 注册一个同类名一样的依赖关系,这个可以省略。
    $container->set('yiidbConnection');
    
    // 注册一个接口
    // 当一个类依赖这个接口时,相应的类会被初始化作为依赖对象。
    $container->set('yiimailMailInterface', 'yiiswiftmailerMailer');
    
    // 注册一个别名。
    // 你可以使用 $container->get('foo') 创建一个 Connection 实例
    $container->set('foo', 'yiidbConnection');
    
    // 通过配置注册一个类
    // 通过 get() 初始化时,配置将会被使用。
    $container->set('yiidbConnection', [
        'dsn' => 'mysql:host=127.0.0.1;dbname=demo',
        'username' => 'root',
        'password' => '',
        'charset' => 'utf8',
    ]);
    
    // 通过类的配置注册一个别名
    // 这种情况下,需要通过一个 “class” 元素指定这个类
    $container->set('db', [
        'class' => 'yiidbConnection',
        'dsn' => 'mysql:host=127.0.0.1;dbname=demo',
        'username' => 'root',
        'password' => '',
        'charset' => 'utf8',
    ]);
    
    // 注册一个 PHP 回调
    // 每次调用 $container->get('db') 时,回调函数都会被执行。
    $container->set('db', function ($container, $params, $config) {
        return new yiidbConnection($config);
    });
    
    // 注册一个组件实例
    // $container->get('pageCache') 每次被调用时都会返回同一个实例。
    $container->set('pageCache', new FileCache);
    
    //注册一个单例的依赖关系
    $container->setSingleton('yiidbConnection', [
        'dsn' => 'mysql:host=127.0.0.1;dbname=demo',
        'username' => 'root',
        'password' => '',
        'charset' => 'utf8',
    ]);
    

    Example

    <?php
    namespace vendordriver;
    
    class Car{
      private $driver;
    
      //其必须是通过Driver这个接口(类)来实例的,如果通过接口的话在注入的时候就可以达到解耦的目的
      public function __construct(Driver $driver){
        $this -> driver = $driver;
      }
    
      public function run(){
        $this -> driver -> run();
      }
    }
    ?>
    
    <?php
    namespace vendordriver;
    
    interface Driver{
      public function run();
    }
    ?>
    
    <?php
    namespace vendordriver;
    
    class WoManDriver implements Driver{
      public function run(){
        echo '当心,这是一个女司机';
      }
    }
    ?>
    
    <?php
    namespace vendordriver;
    
    class ManDriver implements Driver{
      public function run(){
        echo '放心,这是一个男司机';
      }
    }
    ?>
    
    <?php
    namespace appcontrollers;
    use yiiwebController;
    use yiidiContainer;
    
    class IndexController extends Controller{
      public function actionIndex(){
        $container = new Container();
        //设置一个别名
        $container -> set('car','vendordriverCar');
    
        //如果car中的构造方法传入的对象必须是由某个接口而实例的,就还需要使用set方法,否则不需要(如果是类的话注意命名空间的规范既可);
        $container -> set('vendordriverDriver','vendordriverWoManDriver');
    
        //↓↓ 先找到别名,然后实例别名,如果别名不能实例(是个接口),那再通过set注册其依赖关系为接口下面的某个具体的类(究竟是哪个具体的类,可以根据业务逻辑来判断)
        $car = $container -> get('car');
        $car -> run();
      }
    }
    

    解决依赖关系

    // "db" 是前面定义过的一个别名
    $db = $container->get('db');
    
    // 等价于: $engine = new appcomponentsSearchEngine($arg1,$arg2,$arg3 );
    $engine = $container->get('appcomponentsSearchEngine', [$arg1,$arg2,$arg3], ['type' => 1]);
    # question: 但是这里的 ['type' => 1] ??是什么?无解啊
    

    服务定位器

    服务定位器是在应用主体中的一个属性对象,该对象是 yiidiServiceLocator 或其子类的一个实例。

    最常用的服务定位器是 application(应用)对象,可以通过 Yii::$app 访问。它所提供的服务被称为 application components(应用组件),比如: request 、 response 、 urlManager 组件。这些组件在 config/web.php中components中配置

    除了 application 对象,每个模块对象本身也是一个服务定位器

    动态注册

    use yiidiServiceLocator;
    use yiicachingFileCache;
    $locator = new ServiceLocator;
    // 通过一个可用于创建该组件的类名,注册 "cache" (缓存)组件。
    $locator->set('cache', 'yiicachingApcCache');
    // 通过一个可用于创建该组件的配置数组,注册 "db" (数据库)组件。
    $locator->set('db', [
    'class' => 'yiidbConnection',
    'dsn' => 'mysql:host=localhost;dbname=demo',
    'username' => 'root',
    'password' => '',
    ]);
    // 通过一个能返回该组件的匿名函数,注册 "search" 组件。
    $locator->set('search', function () {
    return new appcomponentsSolrService;
    });
    // 用组件注册 "pageCache" 组件
    $locator->set('pageCache', new FileCache);
    
    // 一旦组件被注册成功,你可以任选以下两种方式之一,通过它的 ID 访问它:
    $cache = $locator->get('cache');
    // 或者
    $cache = $locator->cache;
    
    # 你可以通过 yiidiServiceLocator::has() 检查某组件 ID 是否被注册。若你用一个无效的 ID 调用yiidiServiceLocator::get(),则会抛出一个异常。
    
    $locator = new yiidiServiceLocator;
    //设置一个别名
    //locator中的set只负责设置别名
    $locator -> set('car','vendordriverCar');
    //然后通过全局DI容器设置依赖关系
    Yii::$container -> set('vendordriverDriver','vendordriverWoManDriver');
    //$car = $locator -> get('car');
    $car = $locator -> car;
    $car -> run();
    

    静态注册

    直接配置到web.php中

     return
      [
        // ...
        'components' => [
          'db' => [
            'class' => 'yiidbConnection',
            'dsn' => 'mysql:host=localhost;dbname=demo',
            'username' => 'root',
            'password' => '',
          ],
          'cache' => 'yiicachingApcCache',
          'search' => function () {
            return new appcomponentsSolrService;
          },
        ],
      ];
    
    // 首先在web.php中的components数组加上以下元素
    'car' => [
        'class' => 'vendordriverCar'
    ]
    
    //然后通过全局DI容器设置依赖关系(非接口情况下可以省略)
    Yii::$container -> set('vendordriverDriver','vendordriverWoManDriver');
    //$car = $locator -> get('car');
    $car = Yii::$app -> car;
    $car -> run();
    

    依赖注入与服务定位器其实都是一个东西的两种不同表现形式而已,在类似的编程环境中,如果是组件类的话,推荐用服务定位器;如果一些非组件类的话可以用依赖注入;

  • 相关阅读:
    聊聊Mysql索引和redis跳表 ---redis的有序集合zset数据结构底层采用了跳表原理 时间复杂度O(logn)(阿里)
    各种排序算法的时间复杂度和空间复杂度(阿里)
    HashMap默认加载因子为什么选择0.75?(阿里)
    深入分析Synchronized原理(阿里面试题)
    Gflags 简明使用
    析构函数 (C++)
    C++11学习
    Visual Studio Code 构建C/C++开发环境
    C++经典排序算法总结
    C++迭代器失效的几种情况总结
  • 原文地址:https://www.cnblogs.com/nixi8/p/5203156.html
Copyright © 2011-2022 走看看