Методы и атрибуты inject
Эта статья посвящена разным способам передачи зависимостей в
презентеры фреймворка Nette. Мы сравним предпочтительный способ,
внедрение через конструктор, с альтернативами в виде методов и
атрибутов inject.
Для презентеров, как и для других классов, предпочтительным подходом
является передача зависимостей через конструктор.
Однако если вы создаёте общего предка, от которого наследуются другие
презентеры (например, BasePresenter), и этому предку тоже нужны
зависимости, может возникнуть проблема, известная как ад
конструкторов. Её можно обойти альтернативными способами, а именно
методами и атрибутами inject (раньше аннотациями).
Методы inject*()
Это разновидность передачи зависимостей через сеттеры.
Имена таких сеттеров должны начинаться с префикса inject. Nette DI
автоматически вызывает методы с такими именами сразу после создания
экземпляра презентера и передаёт им все нужные зависимости. Поэтому
они должны быть объявлены как public.
Методы inject*() можно воспринимать как расширение конструктора,
разбитое на несколько методов. Это позволяет BasePresenter получать
свои зависимости отдельным методом, оставляя конструктор свободным
для его потомков:
abstract class BasePresenter extends Nette\Application\UI\Presenter
{
private Foo $foo;
public function injectBase(Foo $foo): void
{
$this->foo = $foo;
}
}
class MyPresenter extends BasePresenter
{
private Bar $bar;
public function __construct(Bar $bar)
{
$this->bar = $bar;
}
}
У презентера может быть сколько угодно методов inject*(), и каждый
может принимать сколько угодно параметров. Такой подход хорошо
подходит и для случаев, когда презентер составлен из трейтов и каждому
трейту нужны свои зависимости.
Атрибуты Inject
Это разновидность внедрения в свойства. Достаточно пометить свойства, в которые нужно внедрить зависимости, и Nette DI автоматически передаст их сразу после создания экземпляра презентера. Чтобы внедрение было возможно, эти свойства должны быть объявлены как public.
Свойства помечаются атрибутом (раньше использовалась аннотация
/** @inject */):
use Nette\DI\Attributes\Inject; // эта строка важна
class MyPresenter extends Nette\Application\UI\Presenter
{
#[Inject]
public Cache $cache;
}
Преимуществом этого способа передачи зависимостей была очень краткая запись. Однако с появлением продвижения свойств конструктора использование конструктора часто выглядит проще.
С другой стороны, этот способ страдает теми же недостатками, что и внедрение в свойства вообще: у нас нет контроля над изменениями переменной, а сама переменная становится частью публичного интерфейса класса, что обычно нежелательно.