依赖注入——注入容器,还是独立的依赖?

Dependency Injection - Injecting The Container, Or Individual Dependencies?

使用依赖注入时,应该将依赖项单独传递到构造函数中,还是传递整个 DI 容器?

例如...我有一个名为 'UserRepository' 的存储库。它包含以下方法:

<?php

namespace MyApp\Repositories;

use \MyApp\Models\User;

class UserRepository {

    private $ci;

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

    public function hashPassword($password)
    {
        return password_hash($password, PASSWORD_BCRYPT, [
            'cost' => 15
        ]);
    }

    public function create($firstname, $lastname, $email, $password)
    {
        $user = User::create([
            'firstname' => $firstname,
            'lastname'  => $lastname,
            'email'     => $email,
            'password'  => $this->hashPassword($password)
        ]);

        return $user;
    }

    public function activateUser($userID)
    {
        $user = User($userID);
        $user->email_verified = 1;
        $user->save();

        $verification = $user->verification();
        $verification->is_used = 1;
        $verification->validated_at = $this->ci->get('Carbon')::now();
        $verification->save();
    }
}

Carbon 依赖项可用,因为我已经传入 Pimple 容器。我可以通过这种方式访问​​任何依赖项(只要它们已注册)。

我正在使用 Slim3 来推广这种 DI。但是,在像 Laravel 这样的应用程序中,我看到依赖项被单独传递到构造函数中。

有什么建议吗?

当您将依赖注入容器传递给 class 时,您将其称为 "Service Locator"。使用服务定位器,您的 class 仍然 负责实例化其依赖项,因此您还应该对其进行单元测试。但是怎么办?如果没有服务定位器,您的对象就不可能存在,并且测试它并不容易。如果您将依赖项传递给构造函数,您可以模拟它们。

在你的 class 你有这个:

$verification->validated_at = $this->ci->get('Carbon')::now();

其中 Carbon 是服务名称。现在你应该记住,你注入 class 的服务定位器需要一个具有该名称的服务,它应该 returns Carbon\Carbon class 的一个实例。如果您的服务定位器缺少 Carbon 服务或者它 returns 一个完全不同的对象怎么办?你应该用这样的东西测试它以确保不会破坏任何东西:

$this->assertInstanceOf(Carbon\Carbon::class, $container->get('Carbon'));

更重要的是,如果您想在其他地方重用您的对象,您需要实施特定的服务定位器实施。

通过使用 DIC,您的对象不再负责实例化其依赖项:

$container['user.repository'] = function ($c) {
    return new UserRepository($c['Carbon']);
};

您的 class 可重用性更高,编写测试也更容易。