hyperf框架里的 make 和 new 有什么区别?

求详细点的解答,至今没想清楚

阅读 7k
1 个回答

不仅仅是 hyperf ,现代化一些的框架, Laravel、ThinkPHP 等,这些都有类似的方法,如 Laravel 的 app()app()->make() 等,其都归因为「容器」。

牛刀小试

举个简单的栗子,现在有一个需求,需要从数据库查询数据,需要一个 MySQLRepository 类,你可以在代码里直接写作:new MySQLRepository() 。就可以直接拿到 MySQLRepository 的实例,同样的 app(MySQLRepository::class) 或者 app()->make(MySQLRepository::class) ,也可以拿到这个 MySQLRepository 的实例。

如果只是简单的使用,看不出来有什么区别,都是 new 一个实例,倘若现在, MySQLRepository 不能满足需求了,你需要换到 MongoDBRepository,这时候,如果你使用 new 的方式创建的实例,你就需要把每个地方的 new MySQLRepository 替换为 new MongoDBRepository

这下工作量可就大了,但是如果使用了上面的「容器」,那就可以简单的在容器上添加绑定即可。

app()->bind(MySQLRepository::class, MongoDBRepository::class);

现在,之前写的 app(MySQLRepository::class) ,创建的实例实际上就成了 MongoDBRepository,这就是对容器最基本的使用。当然,这只是举例,实际业务中我们应该使用接口来完成 Repository 的定义。

依赖注入

依赖注入,其实才是这个带来的最大的好处。

举个栗子,常常能看到一些控制器方法中,有一个 Request 的注入,那你有没有想过,为什么我写了个类型,他就有 Request ,我不写他就没有呢?这就是 IoC(依赖注入容器) 在背后给我们做的。

它实现起来并不难,就是利用反射,来获取方法的参数,然后获得参数的类型,再使用容器或获取类型的实例(如果有绑定就返回绑定的),就这样,我们只写了一个简单的类型在那里,在使用的时候就成了实际的对象,这都是 IoC ,给我们做的的。

除此之外,还有更有趣的,绑定时我们还可以绑定一些初始化参数,比如,我们有一个 Ding 类,是一个钉钉机器人的类,一般情况下,我们都需要钉钉机器人的 SECRET 和 TOKEN,看起来像是下面这样。

$secert = config('ding.secert');
$token = config('ding.token');
$ding = new Ding($secert, $token);
$ding->send('Hi');

然后我们每次需要发送消息的时候,都需要来这样操作一次,当然,你可能会想,我可以封装一个方法然后完成这些不就好了,确实也可以,那么,这里用容器来做一下试试。

app()->bind(Ding::class, function(){
    $secert = config('ding.secert');
    $token = config('ding.token');
    return new Ding($secert, $token);
});

现在,这个 Ding 类就可以像在控制器方法里面那样,直接添加一个类型提示,就可以直接获得一个 Ding 实例,然后调用 send 方法了。

//...
class IndexController{
//...
    function index(Ding $ding){
        $ding->send('Hi');
    }
}

相比起封装一个函数,它更加容易被测试,因为我们可以随时替换容器的绑定,进行模拟。

自动依赖处理

依赖倒置,是一个非常好的实践,但是,可能会出现这种情况,
A 依赖 B,B 依赖 C, C 依赖 D, D 依赖 E ....

这时候如果我们想要一个 A 的对象,代码可能就会是这样 new A(new B(new C(new D())))

如果使用容器,就只需要 app(A::class),这样就能完成上面那一堆操作,是的,他会自动帮你递归解析依赖然后注入进去。

当然,实际情况可能比这些复杂,比如 B 可能是 A 的依赖项,也可能是 A2 的依赖项,而且在分别作为这两个的依赖项时,可能需要的参数还不一样,又或者,B 可能有出来依赖一个有类型的 A,还可能依赖一个常量值(基本值),这些都是有可能的,这时候又该怎么办?

这就涉及到了另一个知识点 上下文绑定,刚刚看了 Hyperf 的文档,没有提到这一块的内容,看了一下代码, hyperf/di 貌似也没有实现这个,那么可以参考一下 Laravel 文档中的 上下文绑定

使用 app 或者 app()->make() 的好处除了上述的外,还有一些 new 不太具备的,比如更加容易的测试。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题