По этой теме было написано много постов (в подавляющем большинстве), но я просто хотел поделиться своими двумя центами и написать короткий пост о том, как я использую шаблон создания объектов 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. Требуется также сделать его статическим, поскольку экземпляр компоновщика должен жить независимо от окружающего класса. Мне нравится этот шаблон, потому что он имеет следующие преимущества:
- Большая инкапсуляция объектов: я могу легко навязать конструкцию объекта с помощью компоновщиков, сделав конструктор Money закрытым (это просто стилистически). Это полностью скрывает все тонкости создания этого объекта: создание списка, разбор даты и т. Д. С точки зрения пользователя мы получаем объект, который легко создать. Моя иллюстрация очень простая, но представьте более сложные графы объектов.
- Читаемость кода: использование этого шаблона для создания объектов, делает модульные тесты и код очень легкими для чтения и отслеживания.
- Меньше набора текста в долгосрочной перспективе: несмотря на то, что для каждого добавленного атрибута вам нужно добавить дополнительный метод компоновщика, объем печати, сохраняемый в долгосрочной перспективе, оправдывает себя.
Вывод
Использование беглого паттерна создания — это более сложная работа, но в конечном итоге его преимущества окупаются. Это делает экземпляры объектов очень элегантными и чистыми. Вам не нужно использовать его с объектами-значениями , большинство преимуществ использования Fluent Object Creation заключается в том, что вам нужно строить довольно сложные графы объектов, но я хотел показать, что он также может подойти объекты малого значения.
Ссылка: Свободное создание объектов от нашего партнера JCG Луиса Атенсио в блоге Reflective Thought .