依赖注入模式 Dependence Inversion Pattern

未匹配的标注

定义

依赖注入是指不是由内部生产(初始化、构造函数 __construct 中通过工厂方法、自行手动 new),而是由外部以参数或其他形式注入的。

解释

简单来说就是把依赖对象的实例化过程交给第三方(IOC容器)来实现。
控制 : 依赖对象实例化的控制。
正向 : 我们在对象中主动获取依赖对象。
反向 : IOC容器生成依赖对象,并且把依赖对象传递到资源对象中。资源对象获取依赖对象的方式反转了,由主动创建变成被动接受了。
IOC容器 :  一个类,它负责控制所有对象的生命周期和各个对象之间的关系。

结构中包含的角色

  1. IocContainer (IOC容器)
  2. AbstractResource (抽象资源))
  3. Resource (具体资源)
  4. AbstractResourceDependent (抽象资源依赖)
  5. ResourceDependent (资源依赖)

最小可表达代码

class ResourceDependent {}

class Resource 
{
    private $resourceDependent;

    public function __construct(ResourceDependent $resourceDependent)
    {
        $this->resourceDependent = $resourceDependent;
    }
}

// 依赖注入
$resource = new Resource(new ResourceDependent);

简化版Laravel服务容器代码

abstract class AbstractResourceDependent {}

class ResourceDependent extends AbstractResourceDependent {}

abstract class AbstractResource
{
    private $resourceDependent;

    public function __construct(AbstractResourceDependent $resourceDependent)
    {
        $this->resourceDependent = $resourceDependent;
    }

    public function debug()
    {
        return 'test';
    }
}

class Resource extends AbstractResource {}

class IocContainer 
{
    // 绑定的数据
    protected $bindings = [];

    // 实例化的数据
    protected $instances = [];

    // 绑定数据
    public function bind($abstract, $concrete = null)
    {
        // 包装成闭包
        if (! $concrete instanceof Closure) {
            $concrete = $this->getClosure($abstract, $concrete);
        }

        $this->bindings[$abstract] = compact('concrete');
    }

    // 包装成闭包的方法
    protected function getClosure($abstract, $concrete)
    {
        return function ($container) use ($abstract, $concrete) {
            if ($abstract == $concrete) {
                return $container->build($concrete);
            }

            return $container->make($concrete);
        };
    }

    // 解析数据
    public function make($abstract)
    {
        // 已经有,直接返回
        if (isset($this->instances[$abstract])) {
            return $this->instances[$abstract];
        }

        // 获取绑定的数据
        $concrete = $this->getConcrete($abstract);

        $object = $this->build($concrete);

        return $object;
    }

    // 创建数据
    public function build($concrete)
    {
        if ($concrete instanceof Closure) {
            return $concrete($this);
        }

        // 暂时屏蔽反射生成的逻辑
        // $reflector = new ReflectionClass($concrete);
        return new $concrete;
    }

    // 获取绑定的数据
    protected function getConcrete($abstract)
    {
        if (isset($this->bindings[$abstract])) {
            return $this->bindings[$abstract]['concrete'];
        }

        return $abstract;
    }
}

$app = new IocContainer;

$app->bind(AbstractResourceDependent::class, function ($app) {
    return new ResourceDependent();
});

$app->bind(AbstractResource::class, function ($app) {
    return new Resource($app->make(AbstractResourceDependent::class));
});

$resource = $app->make(AbstractResource::class);

var_dump($resource->debug());

实际应用场景

  1. Laravel的服务容器
  2. Spring的服务容器

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 0
发起讨论 只看当前版本


暂无话题~