Статьи

Могу ли я сделать слишком много инъекций зависимостей?

Инъекции зависимостей — их может быть слишком много?

Рассмотрим этот сценарий …

У вас есть возможность поработать над новым API для вашего клиента. Директива (или ваше решение) заключается в использовании Spring Boot и внедрении зависимостей. Вы начинаете программировать — вдохновленный и взволнованный для нового проекта.


Вам также может понравиться:
Spring Core: инъекция зависимостей

Как разработчик или дизайнер, вы хотите, чтобы ваши услуги были максимально сфокусированными. То есть, если у вас есть  CustomerService класс, он не работает с каким-то виджетом в вашем приложении. Вы решаете, что  WidgetService класс будет обрабатывать такие вещи. Кажется логичным

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

Чтобы проиллюстрировать, вот пример:

services/
├─ AccountService.java
├─ CustomerService.java
├─ ...
├─ SalesService.java
├─ UserService.java
└─ WidgetService.java

Вы гордитесь Это приложение будет действительно легко поддерживать и поддерживать.

Тогда все усложняется

Представьте, что список услуг — это больше, чем пять простых классов, указанных выше. Может быть, когда вы закончите первоначальный проект, будет пятьдесят или даже сто классов обслуживания.

Теперь вы подошли к тому моменту, когда вам нужно внедрить сервис, который обеспечивает межсервисную функциональность. Например, какой-то глобальный аспект отчетности в приложении?

Ваш  ReportService класс представлен как интерфейс. Класс реализации может выглядеть так:

@Service
public class ReportServiceImpl implements ReportService {
private AccountService accountService;
private CustomerService customerService;
private DService dService;
private EService dService;
private FService fService;
private HService hService;
private IService iService;
private JService jService;
private MoreServicesService moreServicesService;
private SoMuchTypingService soMuchTyingService;
private SalesService salesService;
private UserService userService;
private WidgetService widgetService;

public ReportServiceImpl(@Lazy AccountService accountService, @Lazy CustomerService customerService, 
                         @Lazy DService dService, @Lazy EService eService, @Lazy FService fService, 
                         @Lazy GService gService, @Lazy HService hService, @Lazy IService idService, 
                         @Lazy JService jService, @Lazy MoreServicesService moreServicesService, 
                         @Lazy SooMuchTypingService soMuchTypingService, @Lazy SalesService salesService
                         @Lazy UserService userService, @Lazy WidgetService widgetService) {
    this.accountService = accountService;
    this.customerService = customerService;
    this.dService = dService;
    this.eService = eService;
    this.fService = fService;
    this.gService = gService;
    this.hService = hService;
    this.iService = iService;
    this.jService = jdService;
    this.moreServicesService = moreServicesService;
    this.soMuchTypingService = soMuchTypingService;
    this.salesService = salesService;
    this.userService = userService;
    this.widgetService = widgetService;
}

/**
     * {@inheritDoc}
     */
    @Override
    public List<SomeObject> someReportingServiceMethod() {
    // Here we go ....
    }
}

В приведенном выше примере происходит довольно много инъекций зависимостей. Это не обязательно должны быть услуги. Фактически, большинство из них могут быть инъекциями в интерфейсы, определенные как расширения  JpaRepository API.

Однако инструменты статического анализа (например, CheckStyle) могут сообщать о слишком большом количестве параметров метода или конструктора . С CheckStyle по умолчанию равно 7, что представляет примерно половину параметров в  ReportServiceImpl()  конструкторе.

ReportServiceImpl Класс будет не строить , если функциональность CheckStyle по умолчанию требуется передать в трубопроводе CI / CD.

Обоснование подхода

Для примера  ReportServiceпредположим, что существует бизнес-потребность охватить все эти аспекты приложения (либо через службу, либо через хранилище), чтобы предоставить ожидаемые результаты клиенту или клиенту.

Было потрачено время, чтобы убедиться, что каждый сервис детализирован и сфокусирован на выполнении аспектов, соответствующих названию сервиса. Опять же, убедившись, что  CustomerService не делает то, что  WidgetService должен делать.

При использовании службы отчетов необходимо использовать каждую из внедренных служб, чтобы получать данные, которые будут отражены в отчетах. Как разработчик, я мог бы размыть линии и сказать, что «все отчетные данные будут храниться в службе отчетов», но мне все равно придется отказаться от внедрения интерфейсов репозитория для доступа к фактическим данным.

При нестандартном подходе альтернативный подход может обременять клиента выполнением необходимых вызовов в каждой службе API и созданием отчета на стороне клиента. Хотя это, безусловно, возможно, некоторые пункты для рассмотрения отмечены ниже:

  • Возможность иметь двойную логику отчетности в нескольких клиентских приложениях, что увеличивает вероятность получения неверных данных.

  • Влияние на сетевой трафик для нескольких вызовов API для получения необходимой информации

  • Влияние на производительность для создания логики клиентского процесса, которая, вероятно, лучше подходит для (серверной) обработки на стороне сервера

In the end, the reporting needs (or whatever the driver) must be provided in order to meet the functional needs of the application. Telling the product owner that requirements need to be changed or removed, because there is a goal to not deviate from default properties of a static analysis tool, just may not be acceptable.

Conclusion

Considering the example above, I feel like the best approach toward meeting the needs of the application is to utilize several dependency injections within the public constructor of the service class.

While it may not be common to see as many dependency injections as noted above, I feel like this is a realistic possibility. What I don’t know is, what will be the impact if say one-hundred services or repositories were to be injected into a single service? Does the dependency injection start to fail at some point?

Certainly interested in feedback to this concept. Hopefully, I have established a scenario that provides an example where several injections are required to meet the needs of the application.

Have a really great day!

Further Reading

Spring Core: Dependency Injection

Spring DI Patterns: The Good, the Bad, and the Ugly

How Dependency Injection Works in Spring Java Application Development