Статьи

Аннотация Кошмар

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
@XmlElementWrapper(name="orders")
@XmlJavaTypeAdapter(OrderJaxbAdapter.class)
@XmlElements({
   @XmlElement(name="order_2",type=Order2.class),
   @XmlElement(name="old_order",type=OldOrder.class)
})
@JsonIgnore
@JsonProperty
@NotNull
@ManyToMany
@Fetch(FetchMode.SUBSELECT)
@JoinTable(
    name = "customer_order",
    joinColumns = {
        @JoinColumn(name = "customer_id", referencedColumnName = "id")
    },
    inverseJoinColumns = {
        @JoinColumn(name = "order_id", referencedColumnName = "id")
    }
)
private List orders;

Подождите. Какая? Это действительно то, к чему мы пришли? Я даже не вижу чертову собственность под этим раздуванием. Как это произошло? Да, хорошо — нам пришлось как-то избавиться от ужасов старой конфигурации xml. Но это? Это даже хуже. Предполагается, что этот класс будет чертовым pojo с кучей свойств. Коротко и кратко, легко читается. Мне, как читателю этой статьи, совершенно не интересно, как таблица базы данных объединяет клиентов с заказами. Меня не интересует, как это сериализовано. Это просто детали реализации. Читая этот класс, я живу в объектном мире и хочу знать, какими данными и поведением обладает объект. Не больше, не меньше. На данный момент меня не волнуют имена столбцов, типы выборок или json-сериализация. И я не хочу читать, изменять или перекомпилировать этот класс ради изменения имени таблицы. Я не хочу добавлять еще одну аннотацию для хранения этой сущности в mongoDB. Субъект не должен нести ответственность за эти детали. Мы здесь не только нарушаем принцип единой ответственности, но и несем ответственность.

Хорошо, хорошо, хватит ярости. Как мы решаем эту проблему? Некоторые дублируют сущность для разных слоев с разными целями аннотации. Они отображают сущность на сущность, относящуюся к следующим слоям, используя автоматический картограф, такой как Dozer. Некоторые даже сами пишут это. Но это ни в коем случае не решение. Он просто заменяет один запах кода другим: дублирование.

Поэтому, пожалуйста, сосредоточьтесь на фреймворках, которые не заставляют вас загромождать ваш код. jOOQотличное решение для отображения записей базы данных на объекты без аннотаций. Кроме того, hibernate позволяет вам определять ваши отображения в XML.

Инъекция частного поля

1
2
@Inject
private MyService myService

Это используется довольно часто, хотя это даже не должно быть возможно. Поле myService является закрытым, поэтому оно недоступно извне класса. Тем не менее, это возможно, и люди делают это. На самом деле это взломать. DI-каркас устанавливает поле с помощью отражений, делая setAccessible (true). Вы не хотите взломать в своем коде, не так ли? Давайте посмотрим на альтернативы:

Сеттер Инъекция

Ну, по крайней мере, это лучше, чем внедрение частного поля, поскольку он использует открытый метод вместо взлома частного поля. Но все же, спросите себя: «Этот класс вообще должен жить без введенной ценности?» Потому что, если это не так, нет никаких причин для того, чтобы класс создавался без экземпляра MyService. Вы хотите реализовать это ограничение на уровне класса и внутри конструктора, а не на уровне платформы.

Конструктор Инъекция

Это обычно путь. Это позволяет вам

  • сделать поле неизменным (обычно его не нужно менять).
  • реализовать ограничение, что класс не может быть создан без заданного MyService в нужном месте.

Конечно, это означает, что вы не можете вводить аннотации. Но почему вы хотите? Класс не должен знать, получает ли он инъекции DI-контейнером или классом Factory. Он не должен ничего знать об этом. Нет @Autowired, нет @Qualifier. Все, что нужно знать, это его собственное поведение. Все остальное должно быть обработано за пределами класса.

Можно использовать класс конфигурации или файл для фактического внедрения.

DI-Контейнер — это полезный инструмент, который поможет вам связать ваши классы вместе. Используйте его для этой цели, но не позволяйте ему диктовать ваш код. Дядя Боб написал отличный пост, где объяснил, как использовать DI-Frameworks, не заставляя их диктовать ваш код.

@RunWith (SpringJUnit4ClassRunner.class) в UnitTests

Зачем вам это нужно в юнит-тестах? Потому что он автоматически генерируется вашим IDE / шаблоном приложения? Нет! Вы хотите проверить поведение класса, живущего в изоляции в юнит-тестах. Нет, если DI-Conainer вводит соответствующие поля. Просто введите себе способ настройки. DI-контейнер не требуется. Кстати, все, что делает этот тестраннер, это эти 3 строки кода.

1
2
3
4
private TestContextManager testContextManager;
//..
this.testContextManager = new TestContextManager(getClass());
this.testContextManager.prepareTestInstance(this);

Они не стоят того, чтобы блокировать ваш единственный слот TestRunner. Вы хотите оставить его свободным для тестов с параметризованным параметром @RunWith (JUnitParamsRunner.class) или параллельным @RunWith (ConcurrentJunitRunner.class) .

@Override

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

@SuppressWarnings

… даже не заводи меня

ТЛ; др

Аннотации стали более вредными, чем полезными в наши дни. Мы должны вернуться к pojos и сосредоточиться на том, чтобы сделать наш код максимально беспроблемным и независимым от фреймворка, чтобы сделать его более читаемым и многократно используемым. Не позволяйте фреймворкам определять ваши кодовые базы, поскольку они должны быть заменяемыми инструментами. Остерегайтесь того, что должен знать класс, а что нет. Некоторые аннотации полезны, большинство — нет.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
@DevNull({
 
 @SuppressWarnings
 
 @Autowired,
 @Inject,
 
 @Override
 
 @XmlElementWrapper,
 @XmlJavaTypeAdapter,
 @XmlElement,
 @JsonIgnore,
 @JsonProperty,
 @ManyToMany,
 @Fetch,
 @JoinTable
})

Ссылка: Anmatation Nightmare от нашего партнера по JCG Грегора Риглера в блоге « Будь лучшим разработчиком» .