Внедрять в частное, пакетное или публичное поле или предоставлять сеттер?

Я вижу много примеров 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. Это, однако, имеет большой недостаток: ваш тестовый класс должен использовать получатель внутри поля вместо поля. Много потенциально загнутых шаблонов...