扩展类时的构造函数注入

时间:2012-02-07 11:51:32

标签: php oop dependency-injection symfony

这个问题与Symfony 2没有严格的关系,但是当我使用Symfony 2组件并且稍后可能使用Symfony \ Component \ DependencyInjection \ Container作为DI-Container时,它可能是相关的。

我目前正在使用Symfony 2中的组件构建一个小型库,例如: HttpFoundation,Validator,Yaml。我的域服务都扩展了一个基本的AbstractService,只提供Doctrine \ ORM \ EntityManager和Symfony \ Component \ Validator \ Validator,如下所示:

abstract class AbstractService
{
    protected $em;

    protected $validator;

    /**
     * @param Doctrine\ORM\EntityManager $em
     * @param Symfony\Component\Validator\Validator $validator
     */
    public function __construct(EntityManager $em, Validator $validator)
    {
        $this->em = $em;
        $this->validator = $validator;
    }
}

扩展此AbstractService的Service类现在可能需要注入其他组件,例如Symfony \ Component \ HttpFoundation \ Session。我这样做是这样的:

class MyService extends AbstractService
{
    /**
     * @var Symfony\Component\HttpFoundation\Session
     */
    protected $session;

    /**
     * @param Symfony\Component\HttpFoundation\Session $session
     * @param Doctrine\ORM\EntityManager $em
     * @param Symfony\Component\Validator\Validator $validator
     */
    public function __construct(Session $session, EntityManager $em, Validator $validator)
    {
        parent::__construct($em, $validator);
        $this->session = $session;
    }
}

有没有更优雅的方法来解决这个问题,而不必重复父的构造函数参数,例如:通过使用Setter-Injection for Session?

在我看来,当我使用Setter-Injection for Session时,我必须在我的方法中访问之前添加检查,是否已经注入,我想避免。另一方面,我不想“重复”注入所有服务共享的基本组件。

4 个答案:

答案 0 :(得分:3)

另一种方法是不从抽象类型扩展您的服务。将抽象类型更改为真实类,然后将其注入您的服务,例如

class MyService
…
    public function __construct(ServiceHelper $serviceHelper)
    {
        $this->serviceHelper = $serviceHelper;
    }
}

另一个选择是在需要之前不传递依赖关系,例如。

class MyService
…
    public function somethingRequiringEntityManager($entityManager)
    {
        // do something with EntityManager and members of MyService
    }
}

答案 1 :(得分:1)

  

有没有更优雅的方法来解决这个问题,而不必重复父的构造函数参数,例如:通过使用Setter-Injection for Session?

好吧,通过像你提到的那样使用setter-injection。除此之外,我看到了立即问题的两种可能解决方案:

  1. 使父构造函数也接受Session对象,但默认情况下为null。这对我来说很难看,因为父实际上并不需要会话对象,如果你有其他实现,这将导致父构造函数的长参数列表,所以这实际上不是一个选项。

  2. 传入更抽象的对象。无论是针对特定类型对象的ParameterObject,还是仅仅是一个简单的注册表,这都应该有效。再次;不太可取,因为你正在使用依赖注入,你可能已经知道了原因。

  3. 我不得不问;使用当前方式的危害在哪里?我实际上并没有看到缺点。我会继续前进并坚持下去。如果您发现在构造函数中使用了更多参数,请考虑服务的意图是什么以及为什么需要它,可能会将特定的行为移动到您请求的对象上。

答案 2 :(得分:0)

你可以使用单例作为Session类并在任何地方访问它的实例,但是如果你想限制它的用法,那么这可能不是一个选项。 但是再次Session::getInstance()->getStuff似乎与$this->session->getStuff几乎相同所以回答你的问题 - 我不认为这里有很多选择,你的方法似乎很好。

哦,就像戈登说:你不应该改变论点的顺序。

答案 3 :(得分:0)

我在开发Abstract Controller Bundle时遇到了同样的问题 - 当控制器被定义为服务时 - 最终使用the base class中的setter注入。