Внедрять в частное, пакетное или публичное поле или предоставлять сеттер?
Я вижу много примеров Java, использующих инъекцию зависимостей с частными полями без публичного setter, как это:
public SomeClass {
@Inject
private SomeResource resource;
}
Но это плохая идея, когда инъекция должна выполняться вручную, например, в модульных тестах.
Есть несколько возможностей решить эту проблему:
- добавить публичный сеттер:
setSomeResource(SomeResource r)
- сделать поле общедоступным
- защитить защищенный пакет.
Я бы хотел избежать сеттера, так как в нем ничего не происходит. Поэтому я бы предпочел публичный или защищенный пакет. Что вы порекомендуете?
Ответы
Ответ 1
Я предпочитаю сеттер
- легче отлаживать (помещать точку останова в сеттер, а не на доступ/модификацию поля)
- легче зарегистрировать
- проще добавить некоторую проверку (хотя это не всегда лучшее место)
- легче поддерживать двунаправленное обслуживание (хотя контейнер IOC может позаботиться об этом)
- любая другая "ручная AOP" цель
Но это только мое мнение
Ответ 2
Один из способов избежать создания установщика для поля - это использование инъекции конструктора. Это даже позволяет вам объявить поле окончательным.
Это происходит следующим образом:
public class SomeClass {
private final SomeResource resource;
@Inject
public SomeClass(SomeResource resource) {
this.resource = resource;
}
}
Ответ 3
Добавление сеттеров не является оптимальным решением, так как вы добавляете производственный код, который не нужен.
Альтернативой является использование класса Spring ReflectionTestUtils для ввода тестовых зависимостей с помощью отражения, см. http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/test/util/ReflectionTestUtils.html
EDIT (2017): Однако отражение является еще худшим решением, чем добавление сеттеров. Причиной этого беспорядка является тот факт, что Spring позволяет вводить значения без сеттеров или конструкторов. Моя текущая позиция заключается в том, чтобы придерживаться любого из этих методов и избегать использования методов введения черной магии.
Ответ 4
Я рекомендую использовать setter. В этот вопрос - преимущества использования геттеров и сеттеров.
Ответ 5
С помощью ответа на мой (связанный с этим) вопрос:
Как серверы приложений вводят в частные поля?
Я закодировал этот простой пример о том, как вводить без сеттеров.
Возможно, это помогает
//......................................................
import java.lang.annotation.*;
import java.lang.reflect.*;
//......................................................
@Target(value = {ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@interface Inject {
}
//......................................................
class MyClass {
@Inject
private int theValue = 0;
public int getTheValue() {
return theValue;
}
} // class
//......................................................
public class Example {
//......................................................
private static void doTheInjection(MyClass u, int value) throws IllegalAccessException {
Field[] camps = u.getClass().getDeclaredFields();
System.out.println("------- fields : --------");
for (Field f : camps) {
System.out.println(" -> " + f.toString());
Annotation an = f.getAnnotation(Inject.class);
if (an != null) {
System.out.println(" found annotation: " + an.toString());
System.out.println(" injecting !");
f.setAccessible(true);
f.set(u, value);
f.setAccessible(false);
}
}
} // ()
//......................................................
public static void main(String[] args) throws Exception {
MyClass u = new MyClass();
doTheInjection(u, 23);
System.out.println(u.getTheValue());
} // main ()
} // class
Выполнить вывод:
------- fields : --------
-> private int MyClass.theValue
found annotation: @Inject()
injecting !
23
Ответ 6
При использовании инъекции на основе полей вы сталкиваетесь с проблемой, которую вы описываете при тестировании. Кроме того, при инъекции на основе сеттера экземпляр класса может быть создан в неполном состоянии при запуске тестов, если вы забудете установить некоторые из зависимостей. Недавно я практиковал инъекцию конструктора из-за того, что он заставляет вас устанавливать все зависимости, когда вы создаете экземпляр класса во время тестирования. Ответ, высказанный Андре Родригесом, объясняет, как это будет достигнуто.
Ответ 7
Возможные решения:
-
Используйте среду тестирования, совместимую с CDI, например JGlue CDI-Unit. Таким образом, вам не нужен сеттер. Вы только определяете зависимость внутри своих тестов - обычно с использованием Mockito mock object. IMHO это лучшее решение, так как оно не требует от вас ничего лишнего для тестирования.
-
Ввод в конструктор или сеттер. Правильно, вы можете вводить в сеттеры! Подробнее здесь
-
Используйте защищенный сеттер. Простой и работает в каждом случае. Поскольку он защищен, вы можете получить к нему доступ из своего тестового класса (который должен иметь такое же определение пакета, что и ваш тестируемый класс), и никакие другие пакеты не могут получить к нему доступ.
-
Используйте геттер и переопределяйте его при тестировании. В вашем тестовом классе создайте новый внутренний класс, который расширяет тестируемый класс и переопределяет getter. Это, однако, имеет большой недостаток: ваш тестовый класс должен использовать получатель внутри поля вместо поля. Много потенциально загнутых шаблонов...