依赖注入,延迟加载,代理和循环依赖

时间:2013-04-04 20:36:22

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

这是一个与语言无关的问题,但我用PHP编写示例代码。

我们有两个班级:

  1. 用户
  2. UserRepository
  3. UserRepository类处理与DB的交互并将所需的User加载到User对象并返回它。

    在User类中,假设有一个首先没有加载的email属性,只要需要它就会从Database中延迟加载。 但用户类不应该知道 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;
        }
    } 
    

    EmailProxy类

    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 创建的用户对象?

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

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

    • 您会使用工厂吗?

    修改

      

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

    任何代码/评论都将不胜感激。

    谢谢,

2 个答案:

答案 0 :(得分: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)
    }
}

它看起来有点矫枉过正(并且它可能适用)但它允许您非常容易地重构代码,这对于长期应用是有益的。

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

答案 1 :(得分:0)

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

类似的东西:

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创建的?