简述 container —— 反射机制实现

前言#

使用依赖注入实现控制反转来降低代码的耦合性。

依赖注入 DI,控制反转 IOC,解耦等等等 大家探索前进,终走到了这一步,终于让代码灵活、可扩展、低耦合、高内聚,终于可以开心的 coding 了!!

好,现在我要去数据库中查询用户 id 为 1 的用户名。


new User(
    new \Database\MysqlDrive(new \Database\Connection),
    new Builder(),
    new Relations(),
    new Event(new Listener()),
    new Auth(),
    ...
)

stop?我好像有点写不完,我只想获取一个用户名而已!!!

当我使用依赖注入后,所有的依赖都由调用者来处理, 调用者可以控制 User 的行为,但却要做更多的事情。

但我不想多做一丝事情,我希望更加简单,简单到连依赖也能自动处理,解析,自动注入该有多好呀!

题外话:我所理解的最为灵活的 php 开发架构是,php 作为一个中转,将前端和 mysql 连接起来。这样我们就可以灵活到一套代码解决所有所有的问题了。
例如 http://web.dev/users/1?fields=id,username&include=posts:limit(1|5):order(created_at|desc)

示例代码#

我现在有一个 Foo 类,Foo 类中一个 allBarName() 方法。并且依赖于 BarA~BarD 这 4 个类的 name () 方法。如下

<?php

namespace Container\Foo;

use Container\Bar\BarA;
use Container\Bar\BarB;
use Container\Bar\BarC;
use Container\Bar\BarD;

class Foo
{
    private $barA;
    private $barB;
    private $barC;
    private $barD;

    public function __construct(BarA $barA, BarB $barB, BarC $barC, BarD $barD)
    {

        $this->barA = $barA;
        $this->barB = $barB;
        $this->barC = $barC;
        $this->barD = $barD;
    }

    public function allBarName()
    {
        return $this->barA->name().'/'.$this->barB->name().'/'.$this->barC->name().'/'.$this->barD->name();
    }
}

BarA 示例, BarB,BarC,BarD 相同

<?php

namespace Container\Bar;

class BarA
{
    public function name()
    {
        return '彼得·帕克';
    }
}

现在用依赖注入的方式来,调用 allBarName() 方法。

$foo = new \Container\Foo\Foo(
    new \Container\Bar\BarA(),
    new \Container\Bar\BarB(),
    new \Container\Bar\BarC(),
    new \Container\Bar\BarD()
);

echo $foo->allBarName(); // 彼得·帕克/托尼·屎大颗/巴里·艾伦/布鲁斯·韦恩

上面的 code 已经是低耦合的了。
但懒惰的我并不想去手动注入 Foo 的所有依赖,我希望能有一个 Container 来帮我解析 Foo 的所有依赖,然后再把实例返回给我。

那就来吧,实现一个简单的 Container

<?php

namespace Container;

class Container
{
    public static function make($class)
    {
        $reflection = new \ReflectionClass($class);

        $constructor = $reflection->getConstructor();
        $parameters = $constructor->getParameters();

        $instances = [];

        foreach ($parameters as $parameter) {
            $className =  $parameter->getClass()->getName();
            $instances[] = new $className;
        }

        //... 将数组拆封成参数传递给方法, 类似于 call_user_func_array()
        return new $class(...$instances);
    }
}

这是一个简单到不能再简单的依赖解析容器了,但我想它能满足我的需求。

再次调用 allBarName () 方法

$foo = \Container\Container::make(\Container\Foo\Foo::class);
echo $foo->allBarName(); // 彼得·帕克/托尼·屎大颗/巴里·艾伦/布鲁斯·韦恩

oh yeah!成功了

结语#

我想我已经表达了我的想法,我还没有能力也不是很想去实现一个强大灵活的 container,能够知道为什么需要 container 我已经很满足了。

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 2

这个就是流行的设计模式,java 也采用 DI, IOC。所有不能说 TP5 抄袭了 laravel

7年前 评论

@xuanjiang1985 迷之鄙视链而已,思想本身又部分语言.

7年前 评论