Сгенерированные фабрики
Nette DI может автоматически генерировать код фабрик на основе интерфейса, что избавляет вас от написания кода.
Фабрика – это класс, который создает и настраивает объекты. Поэтому он также передает им их зависимости. Пожалуйста, не путайте с паттерном проектирования factory method, который описывает особый способ использования фабрик и не относится к данной теме.
Мы показали, как выглядит такая фабрика, во вводной главе:
Всё, что вам нужно сделать, это создать интерфейс, а Nette DI сгенерирует
его реализацию. Интерфейс должен иметь ровно один метод с именем
create
и объявлять возвращаемый тип:
Итак, фабрика ArticleFactory
имеет метод create
, который создает
объекты Article
. Класс Article
может выглядеть, например,
следующим образом:
Добавьте фабрику в файл конфигурации:
Nette DI создаст соответствующую реализацию фабрики.
Таким образом, в коде, использующем фабрику, мы запрашиваем объект по интерфейсу, а Nette DI использует сгенерированную реализацию:
Параметризированная фабрика
Метод фабрики create
может принимать параметры, которые он затем
передает конструктору. Например, давайте добавим ID автора статьи в
класс Article
:
Мы также добавим параметр в фабрику:
Поскольку параметр в конструкторе и параметр в фабрике имеют одинаковое имя, Nette DI передаст их автоматически.
Расширенное определение
Определение также может быть записано в многострочной форме с
помощью ключа implement
:
При написании таким удлиненным способом можно предоставить
дополнительные аргументы для конструктора в ключе arguments
и
дополнительную конфигурацию с помощью setup
, как и для обычных
сервисов.
Пример: Если бы метод create()
не принимал параметр $authorId
,
мы могли бы указать в конфигурации фиксированное значение, которое
передавалось бы в конструктор Article
:
Или, наоборот, если бы create()
принимал параметр $authorId
, но
он не был частью конструктора и был передан методом
Article::setAuthorId()
, мы бы обратились к нему в секции setup
:
Аксессор
Помимо фабрик, Nette также может генерировать так называемые аксессоры.
Это объекты с методом get()
, которы возвращает конкретный сервис
из DI-контейнера. Повторные вызовы get()
по-прежнему возвращают
один и тот же экземпляр.
Аксессор обеспечивает ленивую загрузку зависимостей. Пусть у нас
есть класс, который записывает ошибки в специальную базу данных. Если
бы в этом классе соединение с базой данных передавалось конструктором
как зависимость, то соединение приходилось бы создавать всегда, хотя
на практике ошибка возникает очень редко, и поэтому соединение обычно
оставалось бы неиспользованным. Вместо этого класс передает метод
доступа, и только при вызове его get()
создается объект базы
данных:
Как создать аксессор? Просто напишите интерфейс, и Nette DI сгенерирует
реализацию. Интерфейс должен иметь ровно один метод с именем get
и объявить возвращаемый тип:
Мы добавим аксессор в файл конфигурации, который также содержит определение сервиса, который он вернет:
Поскольку метод доступа возвращает службу PDO
, а в конфигурации
есть только один такой сервис, он вернет его. Если сервисов данного
типа больше, мы определяем возвращаемый сервис по имени, например
- PDOAccessor(@db1)
.
Несколько фабрик/аксессоров
До сих пор наши фабрики и аксессоры всегда могли производить или
возвращать только один объект. Однако очень легко создать несколько
фабрик в сочетании с аксессорами. Интерфейс такого класса будет
содержать любое количество методов с именами create<name>()
и
get<name>()
, например:
Поэтому вместо того, чтобы передавать несколько сгенерированных фабрик и аксессоров, мы собираемся передать ещё одну сложную фабрику, которая может делать больше.
В качестве альтернативы можно использовать get()
с параметром
вместо нескольких методов:
В этом случае MultiFactory::getArticle()
делает то же самое, что и
MultiFactoryAlt::get('article')
. Однако альтернативный синтаксис имеет
несколько недостатков. Неясно, какие значения $name
поддерживаются, и нельзя указать тип возврата в интерфейсе при
использовании нескольких различных значений $name
.
Определение списка
Этот способ можно использовать для определения нескольких фабрик в конфигурации:
Или в определении фабрики мы можем ссылаться на существующие сервисы с помощью ссылки:
Определения с использованием тегов
Второй вариант — использовать теги для определения: