极简架构模式-依赖注入模式

依赖注入 - Dependence Inversion - DI

定义

依赖注入是指不是由内部生产(初始化、构造函数 __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的服务容器
本作品采用《CC 协议》,转载必须注明作者和本文链接
Long2Ge
《L05 电商实战》
从零开发一个电商项目,功能包括电商后台、商品 & SKU 管理、购物车、订单管理、支付宝支付、微信支付、订单退款流程、优惠券等
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!