Статьи

Свободное создание объектов

По этой теме было написано много постов (в подавляющем большинстве), но я просто хотел поделиться своими двумя центами и написать короткий пост о том, как я использую шаблон создания объектов Fluent или конструкторы объектов в Java для создания экземпляров Value Objects.
Значения Объекты — это абстракции, которые определяются своим состоянием (значением), а не адресом в памяти. Примерами объектов стоимости являются такие вещи, как деньги, число, координата и т. Д. Они используются не для описания бизнес-объектов, а для описания конкретных неделимых сущностей. Кроме того, они являются отличными кандидатами для добавления их в коллекции и карты.

В Java Value Objects должны быть объявлены как final и не предоставлять методов установки, в основном делая их неизменяемыми после создания, это очень важно

требование. Объявление их окончательными делает их неспособными служить родительскими объектами. Это сделано по замыслу, поскольку объекты значения должны моделировать маленькие и конкретные объекты. Причина в том, что мы должны иметь возможность создавать и сравнивать несколько копий этих объектов, что всегда делается по состоянию, а не по ссылке. Кроме того, вы должны объявить надлежащие методы equals () и hashCode (), чтобы претендовать на правильный объект значения. В C ++ применяются те же принципы. В C ++ вы должны использовать конструктор копирования и перегружать операторы присваивания и сравнения. Шаблон Fluent Object Creation делает создание объекта значения элегантным и чистым. Как мы вскоре увидим, использование беглого создания объектов дает много преимуществ.

Конечный результат применения этого шаблона с точки зрения пользователя API будет выглядеть следующим образом:

01
02
03
04
05
06
07
08
09
10
11
Money fiveEuros = new Money.Builder()
   .currency(Currency.EURO)
   .value(5.0L)
   .countryOfOrigin("Spain")
   .type("Coin")
   .reverse("Map of Europe")
   .obverse("Map of Spain")
   .addColor("Bronze")
   .addColor("Silver")
   .year("1880")
.build();

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
Money fiveEuros = new Money();
fiveEuros.setCurrency(Currency.EURO);
fiveEuros.setValue(5.0L);
fiveEuros.countryOfOrigin("Spain");
fiveEuros.type("Coin");
fiveEuros.reverse("Map of Europe");
fiveEuros.obverse("Map of Spain");
 
List<String> colors = new ArrayList<String>();
for(String color: new String[] {"Bronze", "Silver"}) {
   colors.add(color);
}
 
fiveEuros.setColors(colors);
fiveEuros.setYear("1880");

Который кажется сломанным и имеет много печатания и повторения. На мой взгляд, это пример создания довольно крупного объекта стоимости, большинство из которых, как правило, очень маленькие. Прежде чем мы поговорим о преимуществах создания объектов таким образом, давайте посмотрим на структуру этого шаблона:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
public final class Money {
    
      private Long value;
      private String countryOfOrigin;    
      private Currency currency;
      private String type;
      private String reverse;
      private String obverse;   
      private List<String> colors;
   private Date year;   
  
      private Money() {   }
 
     // -- getters, hashCode, equals -- //
 
     // Static inner Builder class
   public static class Builder {
        private Money _temp = new Money();
public Builder value(Long val) {
_temp.value = val;
return this;
}
public Builder countryOfOrigin(String countryOfOrigin) {
_temp.contryOfOrigin = countryOfOrigin;
         return this;
}
public Builder currency(Currency c) {
_temp.currency = c;
return this;
}
public Builder type(String t) {
_temp.type = t;
return this;
}
public Builder reverse(String r) {
_temp.reverse = r;
return this;
}
 
 
       public Builder obverse(String o) {
_temp.obverse = o;
return this;
}
 
 
public Builder addColor(String c) {
if(_temp.colors == null) {
              _temp.colors = new ArrayList<String>();
           }
           _temp.colors.add(c);
return this;
}
 
         public Builder year(String y) {
              if(y == null || y.isEmpty()) {
                  _temp.year = new Date();
              }
              else {
                  _temp.year = DateFormat.parse(y);
              }
return this;
}
public Money build() {
                // Validate object
               if(Strings.isNullOrEmpty(_temp.name) || _temp.currency == null) {
                  throw new IllegalArgumentException("Coin currency and value required");
               }
return _temp;
}
    }
}

Это тоже дело вкуса, но я предпочитаю статический подход внутреннего класса . Мне нравится канонический характер обращения к застройщику как
Money.Builder. Требуется также сделать его статическим, поскольку экземпляр компоновщика должен жить независимо от окружающего класса. Мне нравится этот шаблон, потому что он имеет следующие преимущества:

  1. Большая инкапсуляция объектов: я могу легко навязать конструкцию объекта с помощью компоновщиков, сделав конструктор Money закрытым (это просто стилистически). Это полностью скрывает все тонкости создания этого объекта: создание списка, разбор даты и т. Д. С точки зрения пользователя мы получаем объект, который легко создать. Моя иллюстрация очень простая, но представьте более сложные графы объектов.
  2. Читаемость кода: использование этого шаблона для создания объектов, делает модульные тесты и код очень легкими для чтения и отслеживания.
  3. Меньше набора текста в долгосрочной перспективе: несмотря на то, что для каждого добавленного атрибута вам нужно добавить дополнительный метод компоновщика, объем печати, сохраняемый в долгосрочной перспективе, оправдывает себя.

Вывод

Использование беглого паттерна создания — это более сложная работа, но в конечном итоге его преимущества окупаются. Это делает экземпляры объектов очень элегантными и чистыми. Вам не нужно использовать его с объектами-значениями , большинство преимуществ использования Fluent Object Creation заключается в том, что вам нужно строить довольно сложные графы объектов, но я хотел показать, что он также может подойти объекты малого значения.

Ссылка: Свободное создание объектов от нашего партнера JCG Луиса Атенсио в блоге Reflective Thought .