依赖注入容器:单个类的多个实例


Dependency Injection Container: Multiple instances of a single class

我刚开始使用依赖注入容器,我遇到了一个问题,使我的代码变得混乱。举个例子:我有一个简单的Container类,它管理多个服务,一个服务可以被"标记"为共享的,所以它只能创建一个实例。

例如,我将为User类制作一个简单的(它不是我实际使用的)依赖注入容器,我会这样做:

class Container
{
    protected $parameters;
    public function set($index, $value)
    {
        $this->parameters[$index] = $value;
    }
    public function getUser()
    {
        return new User($this->paramaters['id'], $this->parameters['nickname']);
    }
}
$container = new Container();
$container->set('id', 1);
$container->set('nickname', 'Bob');
$container->getUser();

这很好用。但现在,这里有另一种情况:假设现在,我想要一个Post类,而不是User类,对于每个帖子,我需要在构造函数中设置它们的标题、文本和日期,并且每个帖子不应该有相同的参数。在这种情况下,依赖注入容器是否经过了调整?如果是,我应该如何在不出现混乱代码的情况下实现它?

编辑:

Will问我的用例:

在我的用例中,我实际上是用一个Router类来实现这一点的,该类需要使用Route实例。每个实例化的Route都需要传递两个参数:模式回调。因此,每次我要求我的容器创建一个Route实例时,它不应该为所有Route使用相同的模式和回调,而是使用一对新的模式(模式+回调)。我该怎么做?

您的目标是创建一个共享抽象,该抽象可以注入到依赖于其数据的代码中。你可以采取两种方法,我认为你已经将两者混合在一起了。

  1. 通用依赖注入容器-在这种情况下,我们制作了一个非常通用的对象,可以存储和检索其他对象。这里有一个例子:

    <?php
    class Container
    {
        protected $dependencies = array();
        public function get($key)
        {
            return isset($this->dependencies[$key]) ? $this->dependencies[$key] : null;
        }
        public function register($key, $value)
        {
            $this->dependencies[$key] = $value;
        }
    }
    

    然后你可以这样使用容器:

    $container = new Container();
    $container->register('user', new User(1, 'Bob'));
    $container->register('somethingElse', new SomethingElse(42));
    

    然后,您可以将$container传递给另一个函数、方法或对象,在那里您可以执行以下操作:

    $user = $container->get('user');
    

    还有许多开源容器可以使用,比如Pimple或PHP-DI。

  2. 特定于域的共享抽象-另一种方法是创建特定的共享抽象,例如,在您的情况下,可能是UserPost

    <?php
    class UserPost
    {
        protected $user;
        protected $post;
        public function __construct(User $user, Post $post)
        {
            $this->user = $user;
            $this->post = $post;
        }
        public function getUser()
        {
            return $this->user;
        }
        public function getPost()
        {
            return $this->post;
        }
    }
    

    然后,您可以简单地注入UserPost并调用$userPost->getUser()/$userPost->getPost()。这里的第二种方法基本上很简单,只需为要传递的数据项组合命名即可。我更喜欢这种方法,因为它使代码更可读,但这是一个意见问题。我更喜欢用通俗易懂的英语将我的类名与"我对这个物体的看法"相对应。在我看来,这也更符合抽象的核心OO原则。但是,选项1中的通用容器是许多人成功使用的方法。