Конструктор-Инъекция при расширении класса - PullRequest
7 голосов
/ 07 февраля 2012

Этот вопрос не имеет прямого отношения к Symfony 2, но, поскольку я использую компоненты Symfony 2 и позже, скорее всего, буду использовать Symfony \ Component \ DependencyInjection \ Container в качестве DI-контейнера, он может иметь отношение.

В настоящее время я создаю небольшую библиотеку, используя компоненты из Symfony 2, например HttpFoundation, Validator, Yaml. Все мои доменные службы расширяют базовую службу AbstractService, обеспечивая только Doctrine \ ORM \ EntityManager и Symfony \ Component \ Validator \ Validator посредством Constructor-Injection, например:

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;
    }
}

Классу Service, расширяющему этот AbstractService, теперь может понадобиться добавить дополнительные компоненты, такие как 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 ]

3 голосов
/ 07 февраля 2012

Альтернативой было бы не расширять ваши Услуги с абстрактного типа.Измените абстрактный тип на реальный класс, а затем внедрите его в свои службы, например,

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 голос
/ 07 февраля 2012

Есть ли более элегантный способ решить эту проблему без повторения аргументов конструктора родителя, например, вместо этого используя Setter-Injection for Session?

Ну, используя сеттер-инъекцию, как вы упоминали. Кроме того, я вижу два возможных решения вашей немедленной проблемы:

  1. Заставить родительский конструктор также принять объект Session, но по умолчанию сделать его пустым. Для меня это ужасно, так как родительский объект на самом деле не нуждается в объекте сеанса, и если у вас есть другие реализации, это приведет к длинному списку параметров для родительского конструктора, так что это на самом деле не вариант.

  2. Перейдите в более абстрактный объект. Будь то ParameterObject для этого конкретного типа объекта или просто реестр, это должно работать. Снова; не является предпочтительным, и поскольку вы используете внедрение зависимостей, вы, вероятно, уже знаете, почему.

Я должен спросить, хотя; где вред в использовании вашего нынешнего пути? На самом деле я не вижу обратной стороны. Я бы просто продолжил и продолжил. Если вы обнаружите, что используете в конструкторе еще больше параметров, подумайте о том, каковы намерения службы и зачем она нужна, скорее всего, отдельные части поведения можно будет перенести на запрашиваемые вами объекты.

0 голосов
/ 07 февраля 2012

Я столкнулся с той же проблемой при разработке моего Abstract Controller Bundle - когда контроллеры определены как сервисы - и в итоге использовал инъекцию сеттера в базовый класс .

0 голосов
/ 07 февраля 2012

Хорошо, вы можете использовать синглтон для класса Session и обращаться к его экземпляру в любом месте, но это может не подойти, если вы хотите ограничить его использование. Но опять же Session::getInstance()->getStuff кажется почти таким же, как $this->session->getStuff, поэтому, чтобы ответить на ваш вопрос - я не думаю, что здесь есть большой выбор, и ваш подход кажется подходящим.

О, и, как сказал Гордон, вы не должны менять порядок аргументов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...