Кинжал 2: Когда использовать инъекции конструктора и когда использовать полевые инъекции?

Я был ленив и использовал почти полностью полевые инъекции. Я просто предоставлял пустой конструктор, поместил поля @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
                    ) {

Ребята, каковы ваши правила выбора между конструктором и полевыми инъекциями? Я что-то упускаю, есть ошибки в моей логике?

Ответы

Ответ 1

Используйте инъекцию конструктора. если вы не можете, используйте инъекцию свойств.

Правило 1 выглядит нормально, например, украшения или атрибуты, которые вы можете использовать для ввода свойств (полей).

Правило 2 выглядит нормально, потому что кто использует ваш класс, он должен следовать за вашим конструктором. Возможно, они не знают, что они тоже должны замаскировать вашу собственность.

Правило 3 Это не только хорошо для модульного теста. Это полезно для применения Single Responsibilty. Легче увидеть ваш графический объект. В противном случае вы скроете его с помощью свойства.

Если мы примем ваш вопрос, да, в вашем конструкторе есть много параметров. Но решение не является инъекцией свойств. Вы можете реорганизовать свой код и использовать агрегированные услуги