В продолжение предыдущей статьи о DSL я предлагаю DslHelper, экспериментальный класс для дальнейшего упрощения кода поддержки DSL.
Рассмотрим этот DSL:
architecture { rules { "beans-web" { } } }
Для реализации "beans-web"()
вызова метода (строки с 3 по 5) мне нужно написать этот код:
class RulesDelegate { private Configuration configuration RulesDelegate(Configuration configuration) { this.configuration = configuration } def methodMissing(String name, Object args) { if (args.length == 1) { if (args[0] instanceof Closure) { Rule rule = new Rule(name) args[0].delegate = new RuleDelegate(rule) args[0].resolveStrategy = Closure.DELEGATE_FIRST args[0]() this.configuration.addRule rule } else { throw new MissingMethodException(name, this.class, args as Object[]) } } else { throw new MissingMethodException(name, this.class, args as Object[]) } } }
В methodMissing()
метод (строки 6 до 23) автоматически вызывается с помощью Groovy всякий раз , когда метод не найден (отсутствует) на объекте. Таким образом, добавляя missingMethod()
к любому классу, вы можете перехватывать все вызовы несуществующих методов, и это именно то, что вам нужно для реализации компоновщика.
Однако methodMissing()
реализация менее привлекательна. Я хочу выполнить свой код, только если я получу один Closure
объект в качестве аргументов. Но так как сигнатура метода methodMissing()
определяется Groovy, я должен проверить количество аргументов и набрать сам.
Если бы я только мог реализовать missingMethod()
это так:
def methodMissing(String name, Closure cl) { Rule rule = new Rule(name) cl.delegate = new RuleDelegate(rule) cl.resolveStrategy = Closure.DELEGATE_FIRST cl() this.configuration.addRule rule }
Это не только меньше кода, но и более читаемый. Увы, Groovy не будет вызывать этот метод. Поэтому нам нужно помочь Groovy ?
Enter DslHelper
, экспериментальный класс, который я написал, который поддерживает строго типизированные methodMissing()
методы!
def methodMissing(String name, Object args) { return DslHelper.methodMissing(this, name, args) } def methodMissing(String name, Closure cl) { Rule rule = new Rule(name) cl.delegate = new RuleDelegate(rule) cl.resolveStrategy = Closure.DELEGATE_FIRST cl() this.configuration.addRule rule }
Позвонив DslHelper.methodMissing()
в methodMissing(String,Object)
методе делегировать вызов methodMissing(String,Closure)
. Для более простых вещей я также могу расширить DslHelper
:
class RulesDelegate extends DslHelper { private Configuration configuration RulesDelegate(Configuration configuration) { this.configuration = configuration } def methodMissing(String name, Closure cl) { Rule rule = new Rule(name) cl.delegate = new RuleDelegate(rule) cl.resolveStrategy = Closure.DELEGATE_FIRST cl() this.configuration.addRule rule } }
DslHelper
предоставляет нестатический methodMissing(String,Object)
метод, позволяющий сосредоточиться на синтаксисе DSL.
Вы можете переопределить methodMissing()
столько раз, сколько захотите, DslHelper
приложит все усилия, чтобы сопоставить аргументы с методом. Возьмем для примера этот DSL:
architecture { rules { "beans-web" { } "web-beans"(ignore: true) { } } }
"web-beans"()
Метод будет проходить два аргумента в methodMissing()
: а Map
и А Closure
(Groovy новообращённых именованные аргументы к Map
объекту).
Вы можете продлить или позвонить DslHelper
и сосредоточиться на methodMissing()
:
class RulesDelegate extends DslHelper { private Configuration configuration RulesDelegate(Configuration configuration) { this.configuration = configuration } def methodMissing(String name, Closure cl) { // implementation } def methodMissing(String name, Map params, Closure cl) { // implementation } }
DslHelper
также поможет вам с необязательными аргументами в вашем синтаксисе DSL:
architecture { rules { "beans-web" { } "web-beans"(ignore: true) { } "io-beans"(silent: true, "option1", "option2") { } } }
Чтобы получить эти String
объекты, добавьте String[]
массив в methodMissing()
( Map
объект должен стоять первым). DslHelper
передаст String
значения в виде String[]
массива, даже если String
передано только одно значение.
class RulesDelegate extends DslHelper { private Configuration configuration RulesDelegate(Configuration configuration) { this.configuration = configuration } def methodMissing(String name, Closure cl) { // implementation } def methodMissing(String name, Map params, Closure cl) { // implementation } def methodMissing(String name, Map params, String[] options, Closure cl) { // implementation } def methodMissing(String name, String[] options, Closure cl) { // added for completeness } }
DslHelper
является экспериментальным, поэтому используйте его на свой страх и риск.
Удачного кодирования!