В предыдущей части Суть: Основы замыканий мы показали, как использовать встроенные методы Groovy для создания прототипа нашего DSL-строителя. В этом посте мы собираемся реализовать наши собственные обработчики замыканий, которые обеспечивают поддержку статической компиляции.
Почему мы вообще должны беспокоиться о статической компиляции в Groovy? Во-первых, он работает быстрее и дает вам еще один уровень уверенности, но главная причина в том, что простое практическое правило:
Если код можно скомпилировать статически, IDE поймет ваш код и даст вам все ожидаемые подсказки.
Хотя Cédric Champeau уже много лет назад реализовал статическую компиляцию в языке Groovy, многие разработчики до сих пор игнорируют эту функцию.
При разработке построителей DSL в Groovy есть три ключевых момента, которые могут увеличить опыт разработчиков, если наш метод принимает Closure
:
- Использование аннотации @DelegatesTo для определения типа делегата и стратегии делегирования.
- Использование аннотации @ClosureParams для определения типа параметров замыкания (будет обсуждаться в Иссушение: Сохранение кода в сухом состоянии)
- Использование параметра type аргумента Closure (будет обсуждаться в The Desiccation: Сохранение кода СУХИМ)
Давайте взглянем на другую итерацию нашего YUML DSL:
Diagram.build { note('You can stick notes on diagrams too!', 'skyblue') aggregation('Customer', 'Order') { source '1' destination '0..*', 'orders' } composition('Order', 'LineItem') { source '*' destination '*' } association('Order', 'DeliveryMethod') { destination '1' } association('Order', 'Product') { source '*' destination '*' } association('Category', 'Product') { bidirectional true } type 'National' inherits from type 'DeliveryMethod' type'International' inherits from type 'DeliveryMethod' }
В последней версии DSL используются две новые функции.
- DSL построителя древовидной структуры с использованием замыканий Groovy
- Свободный DSL с использованием выражений цепочки команд
Методы note
и relationship
теперь принимают замыкания в качестве последнего аргумента, что позволяет нам поместить их за скобки вызова метода. Аргументы закрытия помечены аннотацией DelegatesTo
, чтобы дать подсказки для статического компилятора. Они по-прежнему используют with
method внутри:
Relationship relationship( String source, RelationshipType relationshipType, String destination, @DelegatesTo( value = Relationship, strategy = Closure.DELEGATE_FIRST ) Closure additionalProperties = Closure.IDENTITY ) { Relationship relationship = new Relationship( type(source), relationshipType, type(destination) ) relationship.with additionalProperties this.relationships.add(relationship) return relationship }
Распространенной ошибкой является забывание установки
strategy
наClosure.DELEGATE_FIRST
, так как по умолчаниюstrategy
аннотацииDelegatesTo
равноClosure.OWNER_FIRST
.
Свободная часть DSL использует некоторые вспомогательные объекты, чтобы сохранить ритм метод-параметр-метод-параметр -*. Например, Type
теперь предоставляет метод inherits
, который возвращает InheritanceBuilder
InheritanceBuilder inherits(From from) { return new InheritanceBuilder(diagram, this) }
Класс InheritanceBuilder
довольно прост:
@CompileStatic class InheritanceBuilder { private final Type source private final Diagram diagram InheritanceBuilder(Diagram diagram, Type destination) { this.source = destination this.diagram = diagram } Relationship type(String destination) { return diagram.inheritance(source.name, destination) } }
Этим вспомогательным классам всегда нужно сохранять некоторые обратные ссылки, чтобы помочь построить желаемый объект.
Последние части головоломки — это методы класса Diagram
, которые возвращают ключевые слова, такие как from
:
static From getFrom() { return From.FROM }
Код доступен на GitHub под тегом 02-closures
:
git clone https://github.com/musketyr/yuml-dsl-builder.git cd yuml-dsl-builder git checkout 02-closures
В следующей части Маскировка: Скрытие реализации API билдера мы собираемся сделать дизайн нашего DSL билдера чище, разделив данные и часть определения библиотеки.
Содержание
- Концепция: Основная концепция строителей
- Суть: Основы закрытия
- Помощь: Использование аннотаций для статической компиляции
- Маскировка: Скрытие реализации API конструктора
- Иссушение: Сохранение кода СУХИМ
- Ожидания: Важность правильного обращения с владельцем замыканий
- Расширение: Проектирование билдера DSL для расширения
- Отставка: Переписывание Groovy DSL Builder на Java
- Навигация: Использование аннотаций для именованных параметров
- Заключение: Контрольный список для разработчиков Groovy DSL