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();
    

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

  • 相关阅读:
    《算法竞赛入门经典》 例题35 生成元 (Digit Generator, ACM ICPC Seoul 2005,UVa)
    《算法竞赛入门经典》 例题35 生成元 (Digit Generator, ACM ICPC Seoul 2005,UVa)
    《算法竞赛入门经典》 例题35 生成元 (Digit Generator, ACM ICPC Seoul 2005,UVa)
    SVN分支
    SVN分支
    SVN 版本回退
    SVN 版本回退
    如何在excel中取消合并单元格后内容自动填充?
    如何在excel中取消合并单元格后内容自动填充?
    如何让自己像打王者荣耀一样发了疯、拼了命的学习?
  • 原文地址:https://www.cnblogs.com/nixi8/p/5203156.html
Copyright © 2011-2022 走看看