In the previous post The Navigation: Using the annotations for named parameters we have declared some methods accepting named parameters…


Groovy DSL Builders #10: The Conclusion

In the previous post The Navigation: Using the annotations for named parameters we have declared some methods accepting named parameters in our DSL. This final part provides a checklist for anyone who is going to write Groovy builder from a scratch as well as for developers who want to revisit existing ones.

YUML.me Diagram’s Diagram


Let’s summarise what you should learn from this series into two checklists depending if you are creating new builder DSL or revisiting existing one:

Checklist for creating new builder DSL

  1. As long as there is just a tiny option that Java developers would benefit from your builder DSL try to write your code in Java with Groovy-specific code inside extension classes. The extension classes can still be written in Java.
  2. Separate data and definition classes — at least use different interfaces for each situation.
  3. Use Consumer instead of Closure and then in the Groovy extension class declare shadow method accepting Closure. Alternatively, you can use Function to emphasise the expected content of the lambda code. Use ConsumerWithDelegate and FunctionWithDelegate from Groovy Closure Support project to properly handle closures’ owner.
  4. Any Closure parameter should be annotated with DelegatesTo and ClosureParams. Do not forget to set strategy of DelegatesTo annotation to DELEGATE_FIRST.
  5. Consider what is the best return type of the Closure parameters. Try to declare a common interface for the result of every possible statement expected inside the closure’s body.
  6. Provide an extension point for your DSL
  7. Use named arguments only when there is no other option. Always annotate named arguments map with NamedParams. Methods with named arguments should only be defined in Groovy extension classes.

Checklist for existing Groovy builder DSL

  1. Consider providing pure Java variant of your DSL
  2. Separate data and definition classes — at least use different interfaces for each situation.
  3. Ensure every Closure parameter is annotated with DelegatesTo and ClosureParams. Do not forget to set strategy of DelegatesTo annotation to DELEGATE_FIRST.
  4. Ensure top-level owner is propagated into any nested closures. You can use GroovyClosure.cloneWithTopLevelOwner method from Groovy Closure Support project if you decide not to migrate to Consumer or Function.
  5. Consider what is the best return type of the Closure parameters. Try to declare a common interface for the result of every possible statement expected inside the closure’s body.
  6. Provide an extension point for your DSL
  7. Use named arguments only when there is no other option. Always annotate named arguments map with NamedParams.

One general advice for both situations which didn’t fit elsewhere:

_Try to avoid method names which have a special meaning in Groovy. The most toxic in the terms of builder DSL is using the method_ `_get_` _which is basically a shortcut to_ `_propertyMissing_` _fallback. Especially if you haven't resolved the problem with_ `_owner_` _this may result into cryptic exceptions._

The whole YUML DSL code is available on GitHub:

git clone https://github.com/musketyr/yuml-dsl-builder.git


Contents

  1. The Concept: The core concept of builders
  2. The Essence: The closures’ basics
  3. The Aid: Using the annotations for static compilation
  4. The Disguise: Hiding the implementation of the builder API
  5. The Desiccation: Keeping the code DRY
  6. The Expectations: The importance of handling closures’ owner properly
  7. The Extension: Designing your builder DSL for extendability
  8. The Resignation: Rewriting the Groovy DSL builder into Java
  9. The Navigation: Using the annotations for named parameters
10.  [The Conclusion: _The checklist for Groovy DSL builders’ authors_](https://medium.com/p/9d2b961dbc55)