Внедрить в приватное, пакетное или публичное поле или предоставить сеттер? - PullRequest
15 голосов
/ 07 января 2010

Я вижу много примеров Java, использующих внедрение зависимостей с закрытыми полями без открытого установщика, например:

public SomeClass {
  @Inject
  private SomeResource resource;
}

Но это плохая идея, когда инъекцию следует выполнять вручную, например, в модульных тестах.

Есть несколько возможностей решить эту проблему:

  • добавить публичный сеттер: setSomeResource(SomeResource r)
  • сделать поле публичным
  • сделать пакет поля защищенным

Я бы хотел избежать сеттера, так как в нем ничего не происходит. Так что я бы предпочел публичную или защищенную. Что вы порекомендуете?

Ответы [ 7 ]

14 голосов
/ 07 января 2010

Одним из способов избежать создания установщика для поля является использование конструктора. Это даже позволяет вам объявить поле как окончательное.

Это выглядит так:

public class SomeClass {
    private final SomeResource resource;

    @Inject
    public SomeClass(SomeResource resource) {
        this.resource = resource;
    }
}
8 голосов
/ 07 января 2010

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

Альтернативой является использование класса Spring ReflectionTestUtils для внедрения тестовых зависимостей с использованием отражения, см. http://static.springsource.org/spring/docs/2.5.x/api/org/springframework/test/util/ReflectionTestUtils.html

РЕДАКТИРОВАТЬ (2017): Тем не менее, отражение является еще худшим решением, чем добавление сеттеров. Причиной этого беспорядка является тот факт, что Spring делает возможным ввод значений без установщиков или конструкторов. Моя текущая позиция заключается в том, чтобы придерживаться любого из них и избегать практики инъекции черной магии.

7 голосов
/ 07 января 2010

Я предпочитаю сеттер

  • легче отлаживать (ставить точку останова в установщике, а не при доступе / модификации поля)
  • проще войти
  • проще добавить проверку (хотя это не всегда лучшее место)
  • проще поддерживать двунаправленное сопровождение (хотя об этом может позаботиться контейнер IOC)
  • любое другое «ручное АОП» назначение

Но это только мое мнение

4 голосов
/ 07 января 2010

Я рекомендую использовать сеттер. В этот вопрос - это преимущества использования геттеров и сеттеров.

3 голосов
/ 05 апреля 2010

С помощью ответа на мой (связанный с этим) вопрос:

Как серверы приложений внедряются в частные поля?

Я написал этот простой пример того, как вводить без сеттеров. Возможно, это поможет

//......................................................
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
0 голосов
/ 01 августа 2016

Возможные решения для этого:

  • Используйте среду тестирования с поддержкой CDI, например JGlue CDI-Unit . Таким образом, вам не нужен сеттер. Вы определяете зависимость только внутри своих тестов - обычно с помощью фиктивного объекта Mockito. ИМХО, это лучшее решение, так как оно не требует от вас никаких дополнительных усилий для тестирования.

  • Впрыск в конструктор или сеттер. Это верно, вы можете ввести в сеттеры! Подробнее здесь.

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

  • Используйте геттер и переопределите его при тестировании. В своем тестовом классе создайте новый внутренний класс, который расширяет тестируемый класс и переопределяет геттер. Это, однако, имеет большой недостаток: ваш тестовый класс должен использовать геттер внутри, а не в поле. Много потенциально прослушиваемых шаблонов ...

0 голосов
/ 03 июня 2016

При внедрении в полевых условиях вы сталкиваетесь с проблемой, которую вы описываете при тестировании. Также при внедрении на основе метода установки экземпляр класса может быть создан в неполном состоянии при запуске тестов, если вы забудете установить некоторые зависимости. Я практиковал внедрение конструктора совсем недавно, потому что он заставляет вас устанавливать все зависимости всякий раз, когда вы создаете экземпляр класса во время тестирования. Ответ выше Андре Родригес объясняет, как это будет сделано.

...