Как реализовать строгую типизацию домена с использованием аннотаций Java

#java #annotations #strong-typing

#java #аннотации #строгая типизация

Вопрос:

Проблема

Работа с анемичными объектами домена с несколькими внешними ключами, хранящимися как длинные. Попытка защитить от переноса значений с помощью каких-то строго типизированных доменных типов.

Например, учитывая следующий класс обзора продукта:

 public class Review {
  private Long id;
  private Long productId;
  private int rating;
  private String title;
  private String body;
  private Long createdById;
  private Date createdAt;
  //...
}
 

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

 ReviewDto dto = // some DTO that was parsed from JSON for example
Review review = new Review();
review.productId = dto.getCreatedById(); // see transpose error here as typical type checking doesn't catch it
 

Решение (решения)

Наследование

Очевидным решением является реализация простой иерархии классов для типов доменов.

 public class LongId {
  private Long id;
  //...
}
//...
public class ReviewId extends LongId {...}
public class ProductId extends LongId {...}
//...
public class Review {
  private ReviewId id;
  private ProductId productId;
  //...
}
//...
ReviewDto dto = // some DTO that was parsed from JSON for example
Review review = new Review();
review.productId = dto.getCreatedById(); // compile error as types don't match
 

Недостатком этого решения является то, что фактический тип содержится и, следовательно, трудоемко перенаправлять его в / из JSON и в / из базы данных, требуя от меня написания множества пользовательских сериализаторов.

Дженерики

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

 public class LongId<T> {
  private Long id;
  //...
}
//...
public interface ReviewId {}
public interface ProductId {}
//...
public class Review {
  private LongId<ReviewId> id;
  private LongId<ProductId> productId;
  //...
}
//...
ReviewDto dto = // some DTO that was parsed from JSON for example
Review review = new Review();
review.productId = dto.getCreatedById(); // compile error as types don't match
 

Аннотации?

Кто-нибудь справится с этим с помощью аннотаций Java? Что было задействовано?Область документации для аннотаций Java становится скудной, как только вы пройдете примеры hello world. То, что я нашел, дало только мимолетную ссылку на то, что система подключаема и что мне придется написать свой собственный плагин maven для проверки типа. Я очень заинтересован в этом как в возможном решении, поскольку я не ожидаю, что мне придется писать пользовательские сериализаторы среди других шаблонов, поскольку типы представляют собой простые ссылочные типы Java, которые широко поддерживаются большинством библиотек JSON и баз данных.

 @Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.PARAMETER })
@TypeQualifier(applicableTo = Long.class)
public @interface ReviewId {}
//...
public class Review {
  private @ReviewId Long id;
  private @ProductId Long productId;
  //...
}
//...
ReviewDto dto = // some DTO that was parsed from JSON for example
Review review = new Review();
review.productId = dto.getCreatedById(); // **magic** happens here so that both maven and IDE catch this at compile time
 

Комментарии:

1. Аннотации обычно предназначены для аспектного программирования. Почему аннотации, а не строго типизированные поля? Например, класс ProductID, который имеет одно длинное значение.

2. Как объяснено в разделе Решения> Наследование в вопросе, для его анализа в JSON и из базы данных потребуется шаблон.

Ответ №1:

Платформа проверки — это подход, основанный на аннотациях, как вы и просили.

Платформа проверки позволяет указывать свойства домена с аннотациями типов и обеспечивает соблюдение этих свойств во время компиляции. Он используется в таких компаниях, как Amazon, Google, Uber и многих других.

Вы можете использовать предварительно созданную систему типов или создать свою собственную, которая может содержать всего 4 строки кода (пример). Вам не нужно писать подключаемый модуль Maven.

Для получения дополнительной информации см. Руководство Checker Framework .