Недавно я прочитал об @ImplementedBy
аннотации, доступной в Google Guice . Это позволяет программисту определять связь между интерфейсом и его реализацией для будущего использования при внедрении зависимостей. Это пример привязки точно в срок .
Я довольно привык определять явные привязки в моих модулях, используя следующий синтаксис:
bind(SomeInterface.class).to(SomeInterfaceImplementation.class);
Согласно документации, это эквивалентно следующему использованию @ImplementedBy
аннотации:
@ImplementedBy(SomeInterfaceImplementation.class)
public interface SomeInterface {
//method declarations
}
Единственное преимущество, которое я вижу здесь, это то, что код немного короче. В то же время у этого подхода есть недостаток, на который справедливо указывают те же документы:
Используйте
@ImplementedBy
осторожно; это добавляет зависимость времени компиляции от интерфейса к его реализации.
Такая зависимость не может быть проблемой во многих случаях, но я лично вижу ее как запах кода.
Какие варианты использования делают @ImplementedBy
аннотацию полезной?
Один из возможных способов - использовать его в коде библиотеки или фреймворка. Как описано в документации, аннотация может обеспечивать привязку по умолчанию, которая может быть легко заменена явной.
Если тип присутствует как в
bind()
операторе (в качестве первого аргумента), так и в@ImplementedBy
аннотации, используетсяbind()
оператор. Аннотация предлагает реализацию по умолчанию, которая может быть переопределена привязкой.
Таким образом, как разработчик библиотеки, я могу предоставить своим пользователям готовую привязку, которую можно настроить где-нибудь в клиентском коде.
Является ли это единственной причиной существования аннотации? Или мне чего-то не хватает? Могу ли я получить что-нибудь, используя это в коде, который является просто приложением, заботящимся о некоторой бизнес-логике, а не библиотекой / фреймворком, который нужно расширять?
источник
Ответы:
Я думаю , что опасность здесь используется только в
@ImplementedBy
аннотации. Используется надлежащим образом, в сочетании сbind()
инструкциями в вашем модуле и так далее, это нормально.Наличие реализации по умолчанию отлично подходит для тестирования; Вы не обязательно должны явно определять ложную инъекцию каждый раз, когда тестируете класс, который имеет много зависимостей, или если у вас есть класс, от которого зависит много вещей (поэтому вы должны определять макет каждый раз ).
Например, у вас может быть класс:
И тогда
NoOpDataService
это:Вы никогда не будете использовать это в своем реальном коде, очевидно; в вашем модуле Guice вы бы привязали реализацию, которая на самом деле что-то делает. Но все тесты на классах, которые получают инъекцию
DataService
больше не нуждаются в фиктивной привязке.Я согласен с вами, что наличие ваших интерфейсов зависит от вашей реализации, может быть запахом кода; но он также может удалить шаблон кода, чтобы облегчить тестирование. Это не сложно реализовать функцию; и хотя существует небольшой потенциал для злоупотреблений, в конечном итоге последствия не могут быть слишком плохими (служба запускается неожиданно), и это не будет слишком сложно исправить, даже если это произойдет.
источник