Language agnostic 依赖注入、延迟加载、代理和循环依赖

Language agnostic 依赖注入、延迟加载、代理和循环依赖,language-agnostic,dependency-injection,lazy-loading,factory,proxy-pattern,Language Agnostic,Dependency Injection,Lazy Loading,Factory,Proxy Pattern,这是一个与语言无关的问题,但我用PHP编写示例代码 我们有两个班: 用户类 用户存储库类 UserRepository类处理与DB的交互,并将所需的用户加载到用户对象中并返回它 在User类中,假设有一个email属性在第一时间并没有被加载,它将在需要时从数据库延迟加载。 但是User类不应该知道UserRepository类,所以我们在这里创建一个电子邮件代理。它知道用户存储库,当需要电子邮件时,它会从用户存储库中请求 以下是迄今为止的代码: 用户类 用户存储库类 电子邮件代理类 下面是使用示

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

我们有两个班:

  • 用户
  • 用户存储库
  • UserRepository类处理与DB的交互,并将所需的用户加载到用户对象中并返回它

    在User类中,假设有一个email属性在第一时间并没有被加载,它将在需要时从数据库延迟加载。 但是User类不应该知道UserRepository类,所以我们在这里创建一个电子邮件代理。它知道用户存储库,当需要电子邮件时,它会从用户存储库中请求

    以下是迄今为止的代码:

    用户类 用户存储库类 电子邮件代理类 下面是使用示例:

    $user_repository = new UserRepository($db_instance);
    $email_proxy = new EmailProxy();
    $user = new User('my_username', $email_proxy);
    
    到目前为止还不错。 但这里有一个棘手的部分,我需要你的帮助。 从本质上讲,UserRepository应该负责从数据库中获取用户对象,构建用户对象并返回它。如下图所示:

    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
        }
    }
    
    因此,我的问题是如何将电子邮件代理传递给用户对象,该对象由用户存储库创建

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

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

    • 你会使用工厂吗

    编辑 EmailProxy已经知道UserRepository,如果我们也将EmailProxy传递给UserRepository,它将是一个循环依赖项

    如有任何代码/意见,将不胜感激


    谢谢,

    也许是对
    UserRepository::get\u email\u by\u username()
    方法中的
    User
    对象的引用

    比如:

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


    在回复您的评论时,您只需在创建用户时调用UserRepository方法:

    public function get_user_by_username($username)
    {
        $email_proxy = this->get_email_by_username($username);
        return new User($fetched_username, $email_proxy);
    }
    
    然而,这似乎是一个太明显的答案,我怀疑你问的问题与贴出的完全不同。您可能需要重新表述您的问题:

    所以我的问题是如何将EmailProxy传递给 是由UserRepository创建的吗


    这是一个棘手的问题,我想听听人们是如何处理的。同时,以下是我自己的想法

    我将把
    User
    UserRepository
    类的创建放在工厂中

    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);
        }
    }
    
    此类的目的是对服务隐藏工厂。你只提供他们需要的东西

    现在,您的用户存储库应该是这样的

    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)
        }
    }
    
    这似乎有点过火(很可能是),但它允许您非常容易地重构代码,这对长期应用程序是有好处的


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

    谢谢您的回复。但我的问题是UserRepository应该如何知道EmailProxy?因此,当它在“通过用户名获取用户”中创建新用户时,它可以传递它。“用户存储库->通过用户名获取电子邮件()”返回电子邮件字符串,而不是电子邮件代理。UserRepository无权访问“EmailProxy”。问题是EmailProxy知道UserRepository,因为它使用它来获取电子邮件。若我们也将EmailProxy实例传递给UserRepository,那个么它将是一个循环依赖项。谢谢你们的回复。它确实工作得很好,并且可以解耦依赖关系。正如你所说,这有点过分了。我想这取决于你是喜欢易读的代码还是喜欢容易(快速)编写的代码…:)
    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);
        }
    
    }
    
    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);
        }
    }
    
    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)
        }
    }