[深入理解laravel前提] 一步一步从依赖注入和控制反转实现一个超级简单IOC容器 适合小白食用

懵懂时,我一直不理解为什么要有IOC容器这东西,composer不是已经可以实现自动加载了吗?

一个开发框架有很多的类库、实例等等需要管理和调用,IOC容器可以让我们更好的管理我们的类和对象,当前主流的框架已经离不开它了

别走客官,这是学习武林秘籍第一步,想要习得上层武功,必经之路呀,否则你只能去隔壁练葵花宝典了!

学习它之前,我们需要弄清2个概念

  1. 依赖注入
  2. 控制反转
    最后我们将实现一个简单的自动依赖注入的IOC容器

依赖注入

我想 new 一个实例,但是这个实例中还需要 new 另外一个实例,也就是说两个实例对应的类是包含(组合)关系。

别逼逼,上代码

class A {
    private $objB;
    public function __construct (B $obj) {
        $this->objB = $obj;
    }
}
class B {}

//想拿A就得new B,嘿嘿
$instance = new A(new B());

依赖注入表示的是两个类的实例之间的共存关系

控制反转

为什么要有控制反转呢,上面的依赖注入是我们手动完成的,当某个对象需要另外一个对象的时候,我们就直接手动给它 new 一个出来,有没有更加高效的方式呢?IOC 控制反转应运而生,因为不需要你手动new就能自动产生你需要对象,怎么样,很神奇吧!

说到控制反转,我们不得不提的编程语言的反射机制,试想一下 new 一个类的时候,我只要知道类名就能实例化,是不是意味着只要你给我名字,我就控制了你这个类,那么我们称之为反射。

  1. 告诉我你需要的类的名字叫A,我通过一个叫ReflectionClass 来获得他的反射对象$reflector
    $reflector = new ReflectionClass('App\A');
  2. isInstantiable() 检查类是否可实例化,newInstance()获得一个实例
    if ($reflector->isInstantiable()) {
    return $refector->newInstance(); 
    }
    怎么样,能拿到想要的实例吧,试想一下,如果我们把这种好用的特性结合到依赖注入会怎么样呢,是不是意味着我们就不用手动 new 实例了,直接拿到类名,自动 new 呗,还要啥自行车。

一个类实例化肯定会调用构造方法 __construct() 我们抓住这一点

$constructor = $reflector->getConstructor();
$dependencies = $constructor->getParameters();//获得构造方法的参数

你依赖的类这下全部给我在$dependencies中了吧,那么我一个个new 不就来了么

$dependenciesInstances = [];
foreach($dependencies as $dependency) {
    $reflector = new ReflectionClass($dependency);
    $dependenciesInstances[] = $refector->newInstance(); 
}

至此,我们是不是可以通过构造方法自动new依赖的实例了,效率大大的提高。

实现一个很简单的自动依赖注入工厂类

下面我们来写一个简单的自动依赖注入的工厂来解决我们开头说重复 new 的问题

class IocFactory {
    /**
     * make依赖实例
     * @param $class
     * @return object
     * @throws ReflectionException
     */
    public static function make($class) {
        $reflector = new ReflectionClass($class);
        $constructor = $reflector->getConstructor();

        if (is_null($constructor)) {
            if ($reflector->isInstantiable()) {
                return $reflector->newInstance();
            }
        }

        $dependenciesArgs = [];
        $dependencies = $constructor->getParameters();//获得构造方法的参数
        foreach($dependencies as $dependency ) {
            //实例化依赖
            $dependenciesArgs[] = self::make($dependency->getName());
        }
        return $reflector->newInstanceArgs($dependenciesArgs);
    }
}

下面我们声明三个类,A类包含B类包含C类 也就是 A=>B=>C

class A {
    private $b;
    public function __construct(B $b)
    {
        $this->b = $b;
    }
}
class B {
    private $c;
    public function __construct(C $c)
    {
        $this->c = $c;
    }
}
class C {

}

现在可以实例化A,就可以自动帮我们把B和C也实例化

$obj = IocFactory::make(A::class);
var_dump($obj);

结果

object(A)#6 (1) {
  ["b":"A":private]=>
  object(B)#7 (1) {
    ["c":"B":private]=>
    object(C)#8 (0) {
    }
  }
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 4

很好的文章 :thumbsup: ,只是

...
foreach($dependencies as $dependencie) {
    //实例化依赖
    $dependenciesArgs[] = self::make($dependencie->getClass()->getName());
}

这里的 getClass()->getName()getClass() 已经弃用了,直接使用 getName()即可。

还有就是 dependencies 的单数形式是 dependency 不是 dependencie 哈哈

2年前 评论
syxuwen (楼主) 2年前

代码看了那么多是差不多明白了

2年前 评论
janus

:tada:

2年前 评论

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