В продолжение предыдущей статьи о 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 является экспериментальным, поэтому используйте его на свой страх и риск.
Удачного кодирования!