Методы и атрибуты inject

В этой статье мы рассмотрим различные способы передачи зависимостей в презентеры фреймворка Nette. Сравним предпочтительный способ, которым является конструктор, с другими возможностями, такими как методы и атрибуты inject.

И для презентеров действует правило, что передача зависимостей с помощью конструктора является предпочтительным путем. Однако, если вы создаете общего предка, от которого наследуются другие презентеры (например, BasePresenter), и этот предок также имеет зависимости, возникает проблема, которую мы называем constructor hell. Ее можно обойти с помощью альтернативных путей, которые представляют собой методы и атрибуты (аннотации) 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;
}

Преимуществом этого способа передачи зависимостей была очень лаконичная форма записи. Однако с появлением constructor property promotion кажется проще использовать конструктор.

Напротив, этот способ страдает теми же недостатками, что и передача зависимости в свойства в целом: у нас нет контроля над изменениями в переменной, и в то же время переменная становится частью публичного интерфейса класса, что нежелательно.