网站首页 PHP系列 | 依赖注入容器和服务定位器
PHP系列 | 依赖注入容器和服务定位器
时间:2019-09-21 10:56:28 作者:Tinywan 浏览量:12312

依赖注入之-容器

依赖注入(Dependency Injection,DI)容器就是一个对象,它知道怎样初始化并配置对象及其依赖的所有对象。注册会用到一个依赖关系名称和一个依赖关系的定义。依赖关系名称可以是一个类名,一个接口名或一个别名。依赖关系的定义可以是一个类名,一个配置数组,或者一个 PHP 回调。

一、常见注入方式

1、构造方法注入(Constructor Injection)

在参数类型提示的帮助下,DI 容器实现了构造方法注入。当容器被用于创建一个新对象时, 类型提示会告诉它要依赖什么类或接口。容器会尝试获取它所依赖的类或接口的实例, 然后通过构造器将其注入新的对象。例如:

    class Foo

    {

        public function __construct(Bar $bar)

        {

        }

    }

    $foo = $container->get('Foo');

    // 上面的代码等价于:

    $bar = new Bar;

    $foo = new Foo($bar);

2、方法注入(Method Injection)

通常,类的依赖关系传递给构造函数,并且在整个生命周期中都可以在类内部使用。通过方法注入,可以提供仅由类的单个方法需要的依赖关系, 并将其传递给构造函数可能不可行,或者可能会在大多数用例中导致太多开销。

    class MyClass extends \yii\base\Component

    {

        public function __construct(/*Some lightweight dependencies here*/, $config = [])

        {

            // ...

        }

        public function doSomething($param1, \my\heavy\Dependency $something)

        {

            // do something with $something

        }

    }

3、Setter 和属性注入(Setter and Property Injection)

Setter 和属性注入是通过配置提供支持的。当注册一个依赖或创建一个新对象时,你可以提供一个配置, 该配置会提供给容器用于通过相应的 Setter 或属性注入依赖。例如:

    use yii\base\BaseObject;

    class Foo extends BaseObject

    {

        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'),

    ]);

二、直接注入类到容器中

TP5.1 绑定一个类到容器中(第一个参数直接传入类名)

1、使用容器

    // 绑定类库标识

    Container::getInstance()->bindTo(GameService::class);

    // 自动实例化

    $obj = Container::get(GameService::class);

    // 快速调用

    halt($obj->test());

2、使用助手函数

    // 绑定类库标识

    bind(GameService::class);

    // 快速调用(自动实例化)

    $obj = app(GameService::class);

    halt($obj);

Yii2.0 注册一个同类名一样的依赖关系

    $container = new \yii\di\Container;

    // 注册一个同类名一样的依赖关系,这个可以省略。

    $container->set(GameService::class);

三、绑定类标识

TP5.1 可以对已有的类库绑定一个标识(唯一),便于快速调用

1、使用容器

    // 绑定类库标识

    Container::getInstance()->bindTo('game_service2',GameService::class);

    // 快速调用(自动实例化)

    $obj = Container::get('game_service2');

    halt($obj);

2、使用助手函数

    // 绑定类库标识

    bind('game_service',GameService::class);

    // 快速调用(自动实例化)

    $obj = app('game_service');

    // 带参数实例化调用

    $obj = app('game_service',['file']);

    halt($obj->test());

Yii2.0 注册一个别名

    $container = new \yii\di\Container;

    $container->set('foo', 'yii\db\Connection');

    // 创建一个 Connection 实例

    $obj = $container->get('foo');

四、绑定闭包

TP5.1 使用

    // 使用助手函数

    bind('sayHello', function ($name) {

        return 'hello,' . $name;

    });

    echo app('sayHello',['thinkphp']);

    // 使用容器

    Container::getInstance()->bindTo('sayTinywan',function ($age){

        return 'Tinywan is '.$age;

    });

    echo Container::get('sayTinywan',[24]);

Yii2.0 注册一个PHP回调

    // 每次调用 $container->get('db') 时,回调函数都会被执行。

    $container->set('db', function ($container, $params, $config) {

        return new \yii\db\Connection($config);

    });

四、类绑定到接口

// 绑定think\LoggerInterface接口实现到think\Logbind('think\LoggerInterface','think\Log');

使用接口作为依赖注入的类型

    <?php

    namespace app\index\controller;

    use think\LoggerInterface;

    class Index

    {

        public function hello(LoggerInterface $log)

        {

            $log->record('hello,world!');

        }    

    }

或者使用容器

    // 绑定 app\driver\DriverInterface接口实现到 app\driver\YoungDriver

    $driver = Container::getInstance()->bindTo(DriverInterface::class,YoungDriver::class);

    $car = Container::get(Car::class, [$driver]);

    var_dump($car->run());

依赖注入之-服务定位器

服务定位器是一个了解如何提供各种应用所需的服务(或组件)的对象。在服务定位器中, 每个组件都只有一个单独的实例,并通过ID 唯一地标识。用这个 ID 就能从服务定位器中得到这个组件。

TP5.1 使用

1、配置文件 provider.php

系统会自动批量绑定类库到容器中

    // 应用容器绑定定义

    return [

        'game_player' => \app\common\repositories\player\Game::class,

        'random' => \app\common\repositories\game\RandomGroup::class

    ];

2、那类玩家在玩那个游戏

    // 声明那类玩家

    Container::getInstance()->bindTo(PlayerInterface::class,TeacherPlayer::class);

    // 要玩那个游戏

    $game = app('random');

    // 获取玩家实例

    $player = app('game_player');

    // 玩家开始玩什么游戏

    halt($player->play($game));

3、携带参数

    // 声明那类玩家

    Container::getInstance()->bindTo(PlayerInterface::class, StudentPlayer::class);

    // 要玩那个游戏

    $game = app('random');

    // 获取玩家实例

    $parms = ['caode' => 200, 'name' => 'wangwang'];

    $player = app('game_player',['ddd']);

    // 玩家开始玩什么游戏

    halt($player->play($game,$parms));

原文发布于微信公众号 - Tinywan的杂货摊(TinywanIOT)

原文发表时间:2019-08-14


附件下载
最新评论