glaphire
@glaphire
PHP developer

Как отрефакторить классы с неоднородными конструкторами?

Тег Symfony указан, поскольку вопрос касается его Dependency Injection.

Проект на Symfony 5.0.
Как можно оптимально отрефакторить код, в котором есть несколько классов одного потомка, но у них разные конструкторы, при этом создание классов отведено фабрике?
Я хотела уйти от разрозненных конструкторов, но это получается только путем отказа от инъекции в конструкторы.

Условные классы:
<?php

abstract class A {...}

class A1 extends A
{
    public function __construct($a, $b, $c) {/*...*/}
}

class A2 extends A
{
    public function __construct($a, $d, $e) {/*...*/}
}

class AN extends A
{
    public function __construct($a) {/*...*/}
}

class Factory
{
    public function create($type) {
        switch($type) {
            case 1:
                $object =  new A1($a, $b, $c);
            case 2:
                $object =  new A2($a, $d, $e);
            case N:
                $object =  new A1($a);
        }
    }
}


У меня было несколько попыток отрефакторить это решение и все неудачные. Все аргументы $a, $b, $c, $d, $e нужны, они являются неотъемлемыми атрибутами тех классов, в которые инжектятся.
Классы A1...AN отличаются процентов на 20-30 и нет смысла уходить от общего знаменателя, разве что в трейт эту логику вынести, но это ничего не решит.
Я не могу использовать Setter Injection, чтобы вынести параметры $b,$c и $d, $e, потому что это ничего не дает, класс создается внутри фабрики.
Если отказаться от фабрики, то те же параметры $a ...$e придется пробрасывать в сервис, где все A1...AN создаются и используются.
Сейчас у меня костыль в виде того, что все параметры $a...$e пробрасываются в конструктор фабрики, но это нелогично и в будущем раздует ее конструктор еще больше.

Есть ли какое-то более простое решение, которые я упускаю? Буду рада идеям и ссылкам на источники похожих решений
  • Вопрос задан
  • 101 просмотр
Решения вопроса 2
@Flying
Я бы использовал service locator для формирования списка сервисов и передавал его в конструктор фабрики.

Альтернативно можно определить фабрику как service subscriber, тогда формирование service locator'а будет, возможно, ещё проще.

Если же каким-то образом (например через общий интерфейс или статический метод класса-фабрики), определить список сервисов, которые умеет создавать фабрика, то можно создать compiler pass, который будет формировать service locator на этапе компиляции контейнера через анализ их зависимостей. Это чуть сложнее, но не потребует править код при расширении списка создаваемых фабрикой классов.
Ответ написан
Комментировать
glaphire
@glaphire Автор вопроса
PHP developer
Я упустила в описании вопроса то, что мои "неудобные" параметры были скалярными, а не объектами.
Ответ Flying о service subscribers натолкнул на мысль о поисках способа достать только конфиги/параметры из контейнера, а не тащить весь контейнер в свою фабрику - можно использовать Symfony\Component\DependencyInjection\ParameterBag\ParameterBag.
Ссылка на документацию: Symfony 4.1 - Getting container parameters as a service
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
Maksclub
@Maksclub
maksfedorov.ru
Обычно делают REgistry

FooInterface

Service1Foo
Service2Foo
Service3Foo

FooRegistry через теги или через call регаются конкретные сервисы, в фабрике получаем через type (если через call регали с нужным ключом) или в цикле прогоняя каждую зависимость и вызывая supports($type) у каждого из сервиса
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы