2016-11-27 208 views
0

当使用依赖注入时,是否应该将依赖关系分别传递给构造函数,还是可以通过整个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这样的应用程序中,我看到依赖关系被单独传递给构造函数。

有什么建议吗?

+0

当您将整个DI容器作为依赖关系传递时,您称之为“服务定位器”,并且是的,这是一种不好的做法。 – Federkun

+0

@Federkun谢谢,这是有道理的。我相信疙瘩是一个服务定位器,所以现在我坚持这一点。我不知道两者是不同的。看来我并不是唯一一个感到困惑的人:https://www.reddit.com/r/PHP/comments/3x9e48/service_locator_dependency_injection/ – BugHunterUK

+0

不,Pimple不是服务定位器,但是_you可以将它用作Service Locator_ – Federkun

回答

2

当您将依赖注入容器传递给一个类时,将其称为“服务定位器”。使用服务定位器,您的类是仍然负责实例化其依赖关系,因此您也应该单元测试它。但是如何?没有服务定位器,您的对象就不能存在,并且测试它并不容易。如果你将依赖关系传递给构造函数,你可以嘲笑它们。

在你的类你有这样的:

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

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

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

,更重要的是,如果你想重用你的对象别的地方,你需要实现特定的服务定位器实现。

通过使用DIC你的对象是不负责了实例化其依赖:

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

你的类的,更可重复使用和写作测试是更容易。

+0

很好的解释。最后一部分让我感到困惑。在我的情况下,我有'dependencies.php'文件。在你最后一个例子'user.repository'中,我有一个类似的设置。我有一个'Logger'容器,它返回一个新的'Monolog'实例。但是,我仍然使用'$ this-> ci-> get('Logger');'从Services或Actions中访问它们。我应该以另一种方式访问​​Logger吗? – BugHunterUK

+1

不要误解我,服务定位器不是反模式,当我编写symfony的控制器时,我经常使用它。当然,它隐藏了依赖关系,并不是最佳实践,但它是实现这一目标的实用方法。在有意义的地方使用它 – Federkun