Статьи

StateMachine: Строитель-строитель для Groovy, часть 1

Вещи быстро продвигаются на стороне Groovy DSL в эти дни. Я не только в последнее время почти каждый день открываю новый сборщик Groovy, но и продолжаю свои эксперименты, чтобы упростить разработку DSL и сборщиков для Groovy. И я сообщаю вам некоторые интересные результаты.

После моего последнего поста я сделал интересное наблюдение о строителях в Groovy. Каждый строитель может быть помещен в одну из двух категорий: бесконечная глубина или конечные автоматы. XmlSlurper, MarkupBuilder и ObjectGraphBuilder являются примерами создателей бесконечной глубины. Они не навязывают какой-либо конкретный порядок, в котором вызываются методы. В конце концов, фрагменты XML могут принимать любую форму.

Другие компоновщики, такие как AntBuilder, GraphicsBuilder или SwingBuilder, являются конечными автоматами. Их реализации и API-интерфейсы, которые они скрывают, реализуют определенные структуры. Вы не можете вызвать любой метод, который хотите. Вот пример вызова метода AntBuilder, который не имеет смысла:

def ant = new AntBuilder()

ant.delete(file:"myfile.tmp") {
    javac(srcdir:"src", destdir:"build")
}

Не имеет смысла вызывать javac()метод как дочерний delete()метод, и AntBuilder сгенерирует исключение. Это обычно то, что делают конечные автоматы: они обеспечивают заранее определенный поток событий.

Внимание: delete не поддерживает вложенный элемент «javac».

GraphicsBuilder и SwingBuilder проверяют вызовы методов аналогичным образом. Таким образом, AntBuilder, GraphicsBuilder и SwingBuilder реализованы в качестве конечных автоматов. Но эта логика конечного автомата должна была быть реализована их разработчиками. AntBuilder зависит от Ant, чтобы сообщать о несовместимых вызовах методов. GraphicsBuilder и SwingBuilder расширяют groovy.util.FactoryBuilderSupportкласс удобства для реализации сборщиков Groovy.

FactoryBuilderSupportдействительно облегчает проверку вызовов методов, но все же требует, чтобы разработчики построителей реализовали логику проверки. Следовательно, нет реальной государственной машины для написания Groovy-сборщиков. Такой конечный автомат автоматически проверяет, разрешены ли вызовы методов, на основании определения потока. Это потребует от разработчиков сборщика реализации только тел методов.

Это то, что StateMachineделает класс. Это экспериментальный класс удобства для строителей, если хочешь, для строителей. StateMachineтребует от вас определения состояний и разрешенных преобразований между ними. Вот пример конечного автомата, который имитирует структуру простого HTML-файла:

def machine = new StateMachine()

def execution = machine.define {
    html {
        head({
            title().once()
        }).once()
        body({
            p {
                span()
                to("div")
            }
            div {
                to("p")
            }
        }).once()
    }
}

В этом примере создается построитель (объект, назначенный executionпеременной), для которого можно вызывать методы в определении потока. Порядок и иерархия, в которой они определены, должны соблюдаться при вызове методов в компоновщике:

execution.html {
    head {
        title()
    }
    body {
        p {
            span()
            div {
                p {
                    div {
                        p {
                            span()
                        }
                    }
                }   
            }
        }
    }
}
execution.validateTransformations()

Вызов состояния или преобразования, которое не определено, приведет к ошибке. За исключением этого примера ничего не делает. Вызываемые методы не имеют реализации. Давайте изменим это, изменив определение потока в первую очередь.

def machine = new StateMachine()

def execution = machine.define {
    defaultAction = {
        element ->

        element.children().each {
            "${it.name()}"(it)
        }
    }

    html {
        head({
            title().once()
        }).once()
        body({
            p {
                span()
                to("div")
            }
            div {
                to("p")
            }
        }).once()
    }
}

defaultActionСвойство устанавливает действие для всех методов , которые не имеют один. Действие принимает один аргумент, который является элементом XML, возвращаемым XMLSlurper. Затем для каждого дочернего элемента вызывается метод, соответствующий имени дочернего элемента. Давайте назовем этого строителя:

def html = new XmlSlurper().parseText("""
<html>  
    <head>
        <title></title>
    </head>
    <body>
        <p><div></div></p>
        <div><p></p></div>
        <p><span/></p>
    </body>
</html>
""")        

execution."${html.name()}"(html)
execution.validateTransformations()

В следующей части я продемонстрирую StateMachineвозможности сборщика, переписав пример с Правилами архитектуры из моего предыдущего поста . Если вы хотите получить предварительный просмотр, посмотрите StateMachineTest.groovy файл, прикрепленный к этой статье.

Удачного кодирования!