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

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

原文地址:https://www.cnblogs.com/nixi8/p/5203156.html