В предыдущем сообщении Отставка: Переписывание построителя Groovy DSL на Javaмы перенесли наш построитель Groovy DSL на Java. В этой части мы собираемся объявить некоторые методы, принимающие именованные параметры в нашем DSL.

Если вы читали Манифест разработчика Groovy, то знаете, что я считаю злоупотребление картами в Groovy одним из самых больших грехов разработчиков. С другой стороны, я вижу, насколько привлекательными могут быть именованные аргументы. Groovy 2.5 делает использование аргументов Map немного менее болезненным благодаря набору аннотаций Named*.

Прежде всего, давайте обобщим ситуации, в которых имеет смысл использовать Map для сбора именованных аргументов в вашем DSL.

  1. Внутри ваших объектов определения есть несколько полей, помеченных final, и поэтому у объектов нет конструктора с нулевыми аргументами. В этом случае может быть полезно создать метод с сигнатурой method(Map requiredArgs, Closure optionalProperties).
  2. Есть метод с неоднозначными и/или необязательными аргументами

В нашем YUML DSL не существует соответствующего метода, который был бы полезен для первого варианта использования, но методы source и destination внутри определения relationship легко подходят для второго. Было бы более читаемо, если бы мы могли указать cardinality или title в качестве именованных параметров:

aggregation('Customer', 'Order') {
    source cardinality: '1'
    destination cardinality: '0..*', title: 'orders'
}

Теперь давайте посмотрим, как реализованы новые методы:

public static RelationshipDefinition source(
    RelationshipDefinition self,
    @NamedParams({
        @NamedParam(value = CARDINALITY, type = String.class),
        @NamedParam(value = TITLE, type = String.class)
    })
    Map<String, String> cardinalityAndTitle
) {
    return self.source(
        cardinalityAndTitle.get(CARDINALITY),     
        cardinalityAndTitle.get(TITLE)
    );
}

public static RelationshipDefinition destination(
    RelationshipDefinition self,
    @NamedParams({
        @NamedParam(value = CARDINALITY, type = String.class),
        @NamedParam(value = TITLE, type = String.class)
    })
        Map<String, String> cardinalityAndTitle
) {
    return self.destination(
        cardinalityAndTitle.get(CARDINALITY),     
        cardinalityAndTitle.get(TITLE)
    );
}

В класс расширения добавлены новые методы source и destination. Хотя существует больше аннотаций, связанных с именованными аргументами, только NamedParams и NamedParam являются важными. Такие аннотации, как NamedDelegate или NamedVariant, являются просто преобразованием Groovy AST, помогающим построить список именованных аргументов. Использование NamedParams принесет вам дополнительные статические проверки компиляции, поскольку вы можете пометить параметр как требуемый, а начиная с Groovy 2.5.5 вы также сможете пометить NamedParams, чтобы не принимать какие-либо другие ключи, кроме объявленных. Но тем не менее, например, на момент написания IntelliJ IDEA, похоже, не использует эту функцию, поэтому она не даст вам желаемых подсказок, поэтому использование именованных аргументов все же следует рассматривать как инструмент последнего средства.

Код доступен на GitHub под тегом 09-named-parameters:

git clone https://github.com/musketyr/yuml-dsl-builder.git
cd yuml-dsl-builder
git checkout 09-named-parameters

В последней части этой серии Заключение: Контрольный список для авторов построителей Groovy DSLпредставлен контрольный список для создателей нового DSL для построения Groovy, а также для повторного посещения существующих.

Содержание

  1. Концепция: Основная концепция строителей
  2. Суть: Основы закрытия
  3. Помощь: Использование аннотаций для статической компиляции
  4. Маскировка: Скрытие реализации API конструктора
  5. Иссушение: Сохранение кода СУХИМ
  6. Ожидания: Важность правильного обращения с владельцем замыканий
  7. Расширение: Проектирование билдера DSL для расширения
  8. Отставка: Переписывание Groovy DSL Builder на Java
  9. Навигация: Использование аннотаций для именованных параметров
  10. Заключение: Контрольный список для разработчиков Groovy DSL