Статьи

Groovy Goodness: используйте Builder AST Transformation для Fluent API

Начиная с Groovy 2.3, мы можем легко создать свободный API для наших классов с помощью преобразования @Builder AST. Мы можем применить аннотацию к нашим классам, и полученный файл классов будет иметь все необходимые методы для поддержки свободного API. Мы можем настроить способ создания свободного API с различными параметрами аннотации. В коде Groovy мы уже можем использовать метод with чтобы иметь чистый способ установки значений свойств или использовать именованные аргументы конструктора. Но если наши классы нужно использовать из Java, было бы неплохо дать разработчикам Java свободный API для наших классов Groovy.

В следующем примере мы применяем аннотацию @Builder к простому классу Message с некоторыми свойствами. Мы оставляем все в настройках по умолчанию, и тогда полученный файл класса Message будет иметь новый метод builder который возвращает внутренний вспомогательный класс, который мы можем использовать для установки наших свойств. Для каждого свойства свой новый метод с именем свойства, поэтому мы можем установить значение. И, наконец, наш класс содержит build которая будет возвращать новый экземпляр класса Message с правильными значениями для свойств.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
import groovy.transform.builder.Builder
 
@Builder
class Message {
    String from, to, subject, body
}
 
def message = Message
        .builder()  // New internal helper class.
        .from('[email protected]'// Method per property.
        .to('[email protected]')
        .subject('Sample mail')
        .body('Groovy rocks!')
        .build()  // Create instance of Message
 
assert message.body == 'Groovy rocks!'
assert message.from == '[email protected]'
assert message.subject == 'Sample mail'
assert message.to == '[email protected]'

Если мы хотим изменить имена builderMethodName и методов build мы можем использовать параметры аннотации builderMethodName и buildMethodName :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
import groovy.transform.builder.Builder
 
@Builder(builderMethodName = 'initiator', buildMethodName = 'create')
class Message {
    String from, to, subject, body
}
 
def message = Message.initiator()
        .from('[email protected]')
        .body('Groovy rocks!')
        .create()
 
assert message.body == 'Groovy rocks!'
assert message.from == '[email protected]'

Мы видим, что для каждого свойства генерируется соответствующий метод. Мы также можем настроить префикс для сгенерированного имени метода с prefix параметра аннотации. В следующем примере мы определяем префикс, назначаемый для имен методов:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
import groovy.transform.builder.Builder
 
@Builder(prefix = 'assign')
class Message {
    String from, to, subject, body
}
 
def message = Message.builder()
        .assignFrom('[email protected]')
        .assignBody('Groovy rocks!')
        .build()
 
assert message.body == 'Groovy rocks!'
assert message.from == '[email protected]'

Наконец, мы также можем включать и исключать свойства, которые необходимо включить или исключить из нашего свободного API. Мы используем параметры аннотации include и excludes для определения имен свойств. Это может быть список или список имен через запятую.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
import groovy.transform.builder.Builder
 
@Builder(excludes = 'body' /* or includes = 'from,to,subject' */)
class Message {
    String from, to, subject, body
}
 
def message = Message.builder()
        .from('[email protected]')
        .to('[email protected]')
        .subject('Groovy 2.3 is released')
        .build()
 
assert message.from == '[email protected]'
assert message.subject == 'Groovy 2.3 is released'
 
try {
    message = Message.builder().body('Groovy rocks!').build()
} catch (MissingMethodException e) {
    assert e.message.readLines().first() ==
            'No signature of method: static Message.body() is applicable for argument types: (java.lang.String) values: [Groovy rocks!]'
}

Преобразование @Builder AST также проверяет, применяется @Canonical преобразование @Canonical AST к классу. Любые включенные или исключенные свойства, определенные в преобразовании @Canonical , также включаются или исключаются для сгенерированного кода компоновщика.

Мы можем определить стратегию SimpleStrategy с помощью параметра аннотации builderStrategy . Тогда сгенерированный класс не будет иметь отдельного внутреннего класса помощника и метода сборки. prefix умолчанию установлен на set , но мы можем изменить его, если захотим:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
import groovy.transform.builder.Builder
import groovy.transform.builder.SimpleStrategy
 
@Builder(builderStrategy = SimpleStrategy, prefix = 'assign')
class Message {
    String from, to, subject, body
}
 
def message = new Message()
        .assignFrom('[email protected]'// Method per property.
        .assignTo('[email protected]')
        .assignSubject('Sample mail')
        .assignBody('Groovy rocks!')
 
assert message.body == 'Groovy rocks!'
assert message.from == '[email protected]'
assert message.subject == 'Sample mail'
assert message.to == '[email protected]'

Мы увидим другую особенность аннотации @Builder в будущих сообщениях в блоге.

Код написанный с Groovy 2.3.

Ссылка: Groovy Goodness: используйте Builder AST Transformation для Fluent API от нашего партнера по JCG Хьюберта Иккинка в блоге JDriven .