mmghv/dependency-pocket

此包已被废弃且不再维护。未建议替代包。

PHP 中的依赖注入新形式

v0.2.1 2016-09-30 02:10 UTC

This package is auto-updated.

Last update: 2023-05-20 20:41:08 UTC


README

Build Status Latest Stable Version Latest Unstable Version License

PHP 中 "依赖注入" 的新形式

不要与依赖注入容器或类似Symfony DI 容器PimpleLaravel 服务容器混淆。

DependencyPocket不是以这种方式作为DI 容器,它更是一种新的依赖注入形式。本质上,有两种依赖注入方式:构造函数注入setter 注入,我想将这种技术视为第三种类型,即口袋注入

安装

使用 composer

composer require mmghv/dependency-pocket "~0.2"

使用

我们在类中使用这个口袋来持有依赖项,我们首先创建一个新的口袋

use mmghv\DependencyPocket;

// ...

$this->pocket = new DependencyPocket();

然后添加依赖项,我们首先定义它们(定义每个依赖项的名称类型

$this->pocket->define([
    'dep1',  // allow any type
    'dep2' => 'string',  // primitive type
    'dep3' => 'array',  // primitive type
    'dep4' => 'App\Model\Article'  // class or interface
]);

然后设置我们的依赖项(设置依赖项的值)

$this->pocket->set([
    'dep1' => true,
    'dep2' => 'some value',
    'dep3' => [1, 2],
    'dep4' => $myArticle
]);

然后当我们想要获取一个依赖项时,我们只需这样做

$dep = $this->pocket->get('myDep');
// or
$dep = $this->pocket->myDep;

或者我们可以使用属性重载来轻松地从我们的类(或子类)访问依赖项

public function __get($name)
{
    if ($this->pocket->has($name)) {
        return $this->pocket->get($name);
    } else {
        throw new \Exception("Undefined property: [$name]");
    }
}

何时使用及使用技巧

基本上,当你的类有很多依赖项,但并非所有这些依赖项都是必需的,你不想在构造函数中包含所有这些依赖项,但仍需要一种在子类和测试中轻松更改它们的方法时很有用。

想象一下,你有一个具有5个依赖项的类,但其中只有2个是必要的,其他3个可以设置为某些默认值。然后你扩展这个类,子类可以将这两个必要依赖项之一解析为默认值,并需要替换父类中的一个可选依赖项。你需要能够做到这一点,并希望你的最终类在构造函数中只有一个依赖项,但仍然能够从任何未来的子类中更改任何默认值,以及测试中能够模拟任何这些依赖项的能力。

使用DependencyPocket,你可以像下面这样实现这一点,而无需为每个依赖项使用setter,并且在测试中使用依赖项的模拟比使用setter 注入方法更容易

// Manager.php

namespace App\Managers;

use Illuminate\Contracts\Foundation\Application;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Request;
use mmghv\DependencyPocket;

class Manager
{
    protected $pocket;

    /**
     * Define class dependencies.
     */
    protected function defineDependencies()
    {
        if ($this->pocket) {
            return true;
        }

        $this->pocket = new DependencyPocket();

        $this->pocket->define([
            'app'           => 'Laravel\Lumen\Application',
            'model'         => 'Illuminate\Database\Eloquent\Model',
            'validator'     => 'Illuminate\Contracts\Validation\Factory',
            'request'       => 'Illuminate\Http\Request',
            'redirectUrl'   => 'string',
        ]);
    }

    /**
     * Create new manager.
     *
     * @param  Application $app
     * @param  Model       $model
     * @param  array       $dependencyPocket
     */
    public function __construct(Application $app, Model $model, array $dependencyPocket = [])
    {
        $this->defineDependencies();

        $this->pocket->set($dependencyPocket += [
            'app'           => $app,
            'model'         => $model,
            'validator'     => $app->make('validator'),  // default value
            'request'       => $app->make('request'),  // default value
            'redirectUrl'   => $app->make('session')->previousUrl(),  // default value
        ]);
    }
}
// ArticleManager.php

namespace App\Managers;

use Illuminate\Contracts\Foundation\Application;
use App\Models\Article;

class ArticleManager extends Manager
{

    /**
     * Define any additional class dependencies, Declare this function
     * only when you need to define new dependencies.
     */
    protected function defineDependencies()
    {
        if (parent::defineDependencies()) {
            return true;
        }

        $this->pocket->define([
            // define any new dependencies for this class
        ]);
    }

    /**
     * Create new article-manager.
     *
     * @param  Application $app
     * @param  array       $dependencyPocket
     */
    public function __construct(Application $app, array $dependencyPocket = [])
    {
        // always call this first
        $this->defineDependencies();

        // default value for $model dependency
        $model = new Article();

        // call parent construct and pass dependencies
        parent::__construct($app, $model, $dependencyPocket += [
            'validator'     => new CustomValidator(), // replace default dependency value
            // add any new dependencies for this calss, needs to be defined first in 'defineDependencies()'
        ]);
    }
}

然后当我们实例化ArticleManager类时,我们只需要传递一个依赖项,如下所示

$manager = new ArticleManager($app);

但我们也易于替换我们想要的任何默认依赖项,如下所示

$manager = new ArticleManager($app, [
    'model' => $anotherModel,
    'validator' => $anotherValidator
]);

贡献

这是一个相对较新的技术,因此任何贡献(建议、对所使用技术的改进)都受到欢迎。使用PSR-2标准,并应在PR的情况下对任何更改进行测试。

许可 & 版权

版权所有 © 2016,Mohamed Gharib。在MIT许可下发布。