【问题标题】:Dependency Injection, Lazy loading, Proxy and circular dependency依赖注入、延迟加载、代理和循环依赖
【发布时间】:2013-04-04 20:36:22
【问题描述】:

这是一个独立于语言的问题,但我用 PHP 编写示例代码。

我们有两个类:

  1. 用户
  2. UserRepository

UserRepository 类处理与 DB 的交互并将所需的 User 加载到 User 对象中并返回它。

在 User 类中,假设有一个 email 属性一开始就没有加载,它会在需要时从数据库中延迟加载。 但是 User 类不应该知道 UserRepository 类,所以我们在这里创建一个电子邮件代理。它知道 UserRepository 并且无论何时需要电子邮件,它都会从 UserRepository 询问它。

所以这是到目前为止的代码:

用户类

class User
{
    private $username;
    private $email_proxy;
    public function __construct($username,EmailProxy $email_proxy)
    {
        $this->username = $username;
        $this->email_proxy = $email_proxy;
    }

    public function get_username()
    {
        return $this->username;
    }

    public function get_email()
    {
        return $this->email_proxy->get_email($this->get_username());
    }
}

UserRepository 类

class UserRepository
{
    private $db;
    public function __construct($db)
    {
        $this->db = $db;
    }

    /**
     * @param string username
     * @return string email , email address of the user
     */
    public function get_email_by_username($username)
    {
        // interaction with DB, ...
        return $user_email;
    }
} 

邮件代理类

class EmailProxy
{
    private $user_repository;
    public function __construct(UserRepository $repository)
    {
        $this->user_repository = $repository;
    }
    public function get_email($username)
    {
        return $this->user_repository->get_email_by_username($username);
    }
}

这是使用示例:

$user_repository = new UserRepository($db_instance);
$email_proxy = new EmailProxy();
$user = new User('my_username', $email_proxy);

到目前为止一切顺利。 但这是我需要你帮助的棘手部分。 从本质上讲,UserRepository 应该负责从 DataBase 中获取 User 对象,构造 User 对象并返回它。如下所示:

class UserRepository
{
    private $db;
    public function __construct($db)
    {
        $this->db = $db;
    }

    public function get_email_by_username($username)
    {
        // interaction with DB, ...
        return $user_email;
    }

    public function get_user_by_username($username)
    {
        // fetch the User from DB and ...
        return new User($fetched_username) // missing the EmailProxy
    }
}

所以我的问题是如何将 EmailProxy 传递给由 UserRepository 创建的 User 对象?

  • 您是否将 UserProxy 实例注入到 UserRepository 以便可以将其注入到新创建的 User 对象中?

  • 您会为此使用依赖注入容器吗?

  • 你会使用工厂吗?

编辑

EmailProxy 已经知道 UserRepository,如果我们也将 EmailProxy 传递给 UserRepository,它将是循环依赖。

任何代码/cmets 将不胜感激。

谢谢,

【问题讨论】:

标签: language-agnostic dependency-injection lazy-loading factory proxy-pattern


【解决方案1】:

这是一个棘手的问题,我很想听听人们如何处理它。与此同时,这就是我自己的想法。

我会将 UserUserRepository 类的创建放在工厂中

class ServiceFactory
{
    private $instances=[];
    public function createUser($data)
    {
        return new User($data)
            ->setEmailProxy($this->createEmailProxy());

    }

    public function createEmailProxy()
    {
        if (!isset($this->instances['EmailProxy'])) {
            $this->instances['EmailProxy'] = new EmailProxy(
                $this->createUserRepository()
            );
        }
        return $this->instances['EmailProxy'];
    }

    public function createUserRepository()
    {
        if (!isset($this->instances['UserRepository'])) {
            $this->instances['UserRepository'] = new UserRepository(
                $db, 
                $this->createDtoFactory()
               );
        }
        return $this->instances['UserRepository'];
    }

    public function createDtoProvider()
    {
        return new DtoProvider($this);
    }

}

这是DtoProvider 类的代码

class DtoProvider
{
    private $serviceFactory;

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

    public function createUser($data)
    {
        return $this->serviceFactory->createUser($data);
    }

    public function createOtherStuff($data)
    {
        return $this->serviceFactory->createOtherStuff($data);
    }
}

此类的目的是对服务隐藏工厂。你只为他们提供他们需要的东西。

现在你的 UserRepository 应该是这样的

class UserRepository
{
    private $db;
    private $services;
    public function __construct($db, $serviceProvider)
    {
        $this->db = $db;
        $this->services = $serviceProvider;
    }

    public function get_email_by_username($username)
    {
        // interaction with DB, ...
        return $user_email;
    }

    public function get_user_by_username($username)
    {
        // fetch the User from DB and ...
        return $this->services->createUser($data)
    }
}

这似乎有点矫枉过正(可能是这样),但它允许您很容易地重构代码,这对于长期应用很有好处。

我想这完全取决于您是喜欢易于阅读的代码还是易于(快速)编写的代码。

【讨论】:

  • 感谢您的精彩回复。它肯定工作得很好并且解耦了依赖关系。正如你所说,这有点矫枉过正。
  • 我想这取决于您是喜欢易于阅读的代码还是易于(快速)编写的代码... :)
【解决方案2】:

也许是对 UserRepository::get_email_by_username() 方法中的 User 对象的引用。

类似:

public function get_email_by_username(&$username)
{
    // interaction with DB, ...
    $username->setEmail($email_proxy);
}

http://php.net/manual/en/language.references.pass.php


回复您的评论,您可以在创建用户时简单地调用 UserRepository 方法:

public function get_user_by_username($username)
{
    $email_proxy = this->get_email_by_username($username);
    return new User($fetched_username, $email_proxy);
}

但是,这个答案似乎太明显了,我怀疑您问的问题与发布的完全不同。您可能需要重新表述您的问题:

所以我的问题是如何将 EmailProxy 传递给 User 对象 是由 UserRepository 创建的?

【讨论】:

  • 感谢您的回复。但我的问题是 UserRepository 应该如何知道 EmailProxy?所以当它在“get_user_by_username”中创建一个新用户时,它可以传递它。
  • "UserRepository->get_email_by_username()" 返回一个电子邮件字符串,而不是 email_proxy。 UserRepository 无权访问“EmailProxy”。问题是 EmailProxy 知道 UserRepository,因为它使用它来获取电子邮件。如果我们也将 EmailProxy 实例传递给 UserRepository,这将是一个循环依赖。
猜你喜欢
  • 2012-04-12
  • 1970-01-01
  • 1970-01-01
  • 2020-03-14
  • 1970-01-01
  • 1970-01-01
  • 2018-03-03
  • 2012-02-07
  • 1970-01-01
相关资源
最近更新 更多