依赖注入

DI 和IoC是什么

DI 全称: Dependency Injection 依赖注入
IoC 全称: Inversion of Control 控制反转

控制反转是一种设计模式,依赖注入是该设计模式的一种实现.

IoC描述大致如下:

高层模块不应该依赖底层模块,他们都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象。

真实的dependency injection container会提供更多的特性,如

  • 自动绑定(Autowiring)或 自动解析(Automatic Resolution)
  • 注释解析器(Annotations)
  • 延迟注入(Lazy injection)

下面是简单的自动绑定的容易实现代码:

<?php

class A{
    public function doSomeThing(){
        echo __METHOD__ . "|";
    }
}

class B{
    public $a;

    public function __construct(A $a){
        $this->a = $a;
    }

    public function doSomeThing(){
        $this->a->doSomeThing();
        echo __METHOD__ . "|";
    }
}

class C{
    public $b;

    public function __construct(B $b){
        $this->b = $b;
    }

    public function doSomeThing(){
        $this->b->doSomeThing();
        echo __METHOD__ . "|";
    }
}

class Container{
    public $registry = [];

    public function __set($name, $value){
        $this->registry[$name] = $value;
    }

    public function __get($name){
        //return $this->registry[$name]($this);
        if (!isset($this->registry[$name])) {
            throw new Exception('请先设置容器参数 ' . $name);
        }

        return $this->build($this->registry[$name]);
    }

    private function build($className){
        //如果是闭包,直接调用即可
        if ($className instanceof Closure) {
            return $className($this);
        }

        //反射class
        $reflect = new ReflectionClass($className);

        //检查是否能被实例化,排除abstract 和interface
        if (!$reflect->isInstantiable()) {
            throw new Exception($className . " 不能被实例化");
        }
        //获取构造函数
        $constructor = $reflect->getConstructor();

        if (is_null($constructor)) {//无构造函数
            return new $className;
            //或
            //return $reflect->newInstance();
        }

        //获取构造函数的参数
        $params = $constructor->getParameters();
        //获取参数所需的依赖
        $dependencies = $this->getDependencies($params);

        //实例化class,并添加构造函数参数
        return $reflect->newInstanceArgs($dependencies);
    }

    //获取构造函数参数依赖
    private function getDependencies(array $params){
        $dependencies = [];
        foreach ($params as $param) {
            $dependency = $param->getClass();
            if (is_null($dependency)) {//非class 参数
                $dependencies[] = $this->resolveNonClass($param);
            } else {//构造函数依赖是class, 递归的构建class的依赖
                $dependencies[] = $this->build($dependency->name);
            }
        }

        return $dependencies;
    }

    private function resolveNonClass($param){
        // 有默认值则返回默认值
        if ($param->isDefaultValueAvailable()) {
            return $param->getDefaultValue();
        }

        throw new Exception('缺少构造函数参数');
    }
}

//常规调用class C 中的 doSomeThing 方法
echo PHP_EOL . '===old==' . PHP_EOL;
$old = new C(new B(new A()));
$old->doSomeThing();

//依靠依赖注入 调用 class C 中的 doSomeThing 方法
echo PHP_EOL . '===new==' . PHP_EOL;
$di = new Container();//实例化容器
$di->c = C::class;//class C 放在容器中
$di->c->doSomething();//调用  C 中的 doSomeThing 方法, 此时C需要实例化B,B需要实例化A,这些操作都在容器中实现

//依靠依赖注入 调用 class B 中的 doSomeThing 方法
echo PHP_EOL . '===new2==' . PHP_EOL;
$di = new Container();
$di->b = B::class;
$di->b->doSomething();

//TODO 有时间看下TP还有laravel是如何实现的

参考文章:

results matching ""

    No results matching ""