Я был ленив и использовал почти полностью полевые инъекции. Я просто предоставлял пустой конструктор, поместил поля @Inject, все выглядело красиво и просто. Однако у полевых инъекций есть свои компромиссы, поэтому я разработал несколько простых правил, которые помогают мне решить, когда использовать поле и когда использовать инъекции конструктора. Я буду признателен за любые отзывы, если в моей логике есть ошибка или у вас есть дополнительные соображения для добавления.
Сначала уточним, чтобы быть на одной странице:
Инъекция конструктора:
@Inject
public SomeClass(@Named("app version") String appVersion,
AppPrefs appPrefs) {...
То же самое с инъекцией поля:
public class SomeClass {
@Inject
@Named("app version") String mAppVersion;
@Inject
AppPrefs appPrefs;
Правило 1: ДОЛЖНО использовать инъекцию поля, если я не контролирую создание объекта (подумайте об активности или фрагменте в Android). Если какая-то (не-кинжалная) структура создает мой объект и обрабатывает его для меня, у меня нет выбора, кроме как ввести его вручную после получения экземпляра.
Правило 2: ДОЛЖНО использовать инъекцию конструктора, если класс/может использоваться в другом проекте, который не использует кинжал 2. Если другие проекты не используют кинжал, они не могут использовать DI, поэтому пользователю необходимо создать объект "старый" способ с использованием new
.
Правило 3: Встраивание конструктора PREFER при работе с иерархиями классов, потому что легче создавать модульные тесты.
Разъяснение:
Учитывая следующую структуру, которая использует полевую инъекцию:
package superclass;
public class SuperClass {
@Inject
HttpClient mHttpClient;
...
}
,
package differentpackage;
public class SubClass extends SuperClass {
public SubClass() {
}
}
Когда я создаю единичный тест для SubClass
в каталоге test/java/differentpackage
у меня нет выбора, кроме как воспитывать всю инфраструктуру DI, чтобы иметь возможность вводить HttpClient
. Напротив, если бы я использовал конструкторную инъекцию следующим образом:
public class SuperClass {
private final HttpClient mHttpClient;
@Inject
public SuperClass(HttpClient httpClient) {
mHttpClient = httpClient;
}
}
в моем модульном тесте я мог просто:
HttpClient mockHttp = mock(HttpClient.class);
Subclass tested = new Subclass(mockHttp);
// tests
Таким образом, в настоящее время я нахожусь в другом крайнем случае: я склонен в основном полагаться на инъекции конструктора и использовать полевые инъекции только тогда, когда применяется "Правило 1". Единственная "проблема", которую я имею с инжектором конструктора, заключается в том, что конструкторы классов "end" иногда становятся полностью перегруженными параметрами, и они выглядят многословными и уродливыми:
@Inject
public ModelMainImpl(@Named("app version") String appVersion,
AppPrefs appPrefs,
LoginPrefs loginPrefs,
@ForApplication Context appContext,
NetworkInfoProvider networkInfoProvider,
AndroidEventPoster androidEventPoster,
Session session,
ForgeExchangeManager exchangeManager,
HttpFunctionality httpFunctionality,
@Named("base url") String baseUrl,
@Named("forge result producer") ResultProducer<ForgeExchangeResult> resultProducer
) {
Ребята, каковы ваши правила выбора между конструктором и полевыми инъекциями? Я что-то упускаю, есть ошибки в моей логике?