Отрефакторинг классов с неоднородными конструкторами в Symfony может быть достаточно сложной задачей, но важно для поддержки кодовой базы на должном уровне и обеспечения чистоты кода. Такие классы часто называются "классами с зависимостями". В этом ответе я расскажу о нескольких распространенных практиках, которые помогут вам в этом процессе.
1. Внедрение зависимостей с использованием контейнера Symfony:
Одним из основных преимуществ Symfony является его контейнер зависимостей. Контейнер позволяет определить зависимости в конструкторе класса с помощью типизированных аргументов и использовать автоматическое разрешение зависимостей. Это позволяет увязать классы, определяющие зависимости, с классами, которые их используют. При этом все экземпляры классов будут создаваться через контейнер.
Например, рассмотрим следующий конструктор класса Foo
:
public function __construct(Bar $bar, Baz $baz, string $qux) { // ... }
С помощью контейнера можно определить, какие классы будут внедряться в конструктор Foo
:
# services.yaml services: AppBar: # ... определение зависимостей AppBaz: # ... определение зависимостей AppFoo: arguments: $bar: '@AppBar' $baz: '@AppBaz' $qux: 'some value'
После этого можно получить экземпляр класса Foo
из контейнера:
$foo = $container->get(Foo::class);
При этом контейнер автоматически разрешит зависимости и создаст экземпляры классов Bar
и Baz
, передавая их в конструктор Foo
. Параметр $qux
также будет передан без изменений.
Такой подход позволит создать однородные конструкторы, что значительно облегчит работу с классами и их поддержку.
2. Разделение классов:
Вместо того, чтобы использовать один класс с несколькими конструкторами, вы можете рассмотреть возможность разделения его на несколько классов. Каждый из этих классов будет иметь свой однородный конструктор и будет отвечать только за определенные аспекты бизнес-логики. При необходимости эти классы можно объединить в один композитный класс с помощью композиции или агрегации.
Например, рассмотрим следующий класс Foo
с неоднородными конструкторами:
class Foo { public function __construct(string $type, Bar $bar = null, Baz $baz = null) { // ... } }
Мы можем разделить этот класс на два класса, каждый из которых отвечает за свой конструктор:
class FooWithBar implements FooInterface { private $bar; public function __construct(Bar $bar) { $this->bar = $bar; } } class FooWithBaz implements FooInterface { private $baz; public function __construct(Baz $baz) { $this->baz = $baz; } }
После этого мы можем решить, какие объекты Bar
или Baz
будут передаваться в конструктор Foo
, и использовать только соответствующий класс.
3. Использование сборщиков зависимостей:
Еще одним способом работы с классами с неоднородными конструкторами является использование сборщиков зависимостей, таких как PHP-DI
или Symfony DI
. Эти инструменты позволяют внедрять зависимости в конструкторы классов с помощью аннотаций или конфигурационных файлов. В результате у нас получаются однородные конструкторы, которые легко управлять и изменять.
Например, с использованием PHP-DI
мы можем переписать класс Foo
следующим образом:
class Foo { private $bar; private $baz; private $qux; #[Inject] public function __construct(Bar $bar, Baz $baz, #[SomeAnnotation] string $qux) { $this->bar = $bar; $this->baz = $baz; $this->qux = $qux; } }
В этом примере мы используем аннотацию #[Inject]
, чтобы указать сборщику зависимостей, что класс Foo
должен быть создан с указанными зависимостями. Параметр $qux
также имеет аннотацию #[SomeAnnotation]
, чтобы указать, что его значение должно быть взято из соответствующего источника.
При этом в настройках сборщика зависимостей мы можем определить, какие зависимости должны быть внедрены в конструктор класса Foo
.
Это позволяет нам иметь однородные конструкторы и гибкость в управлении зависимостями.
В заключение хотелось бы отметить, что выбор конкретного подхода зависит от требований вашего проекта и предпочтений команды разработчиков. Какой бы подход вы не выбрали, главная цель состоит в том, чтобы сделать код более понятным, модульным и гибким для последующих изменений.