Criando uma DSL de HTML/configuração segura quanto a tipos
Implemente uma DSL simples de configuração usando padrões construtores e lambdas de extensão.
Criando uma DSL de HTML/configuração segura quanto a tipos é uma aula grátis de Kotlin Academy no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Kotlin Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Kotlin Academy inclui 4 aulas no total.
Objetivo: uma DSL prática
Vamos criar uma DSL construtora de HTML mínima, mas realista, em Kotlin. Os mesmos padrões se aplicam a qualquer construtora de configuração, dispositivo de teste ou relatório. O resultado é uma API segura quanto aos tipos e compatível com a IDE, que substitui a concatenação de strings.
Definindo a classe base Tag
Todos os elementos HTML compartilham uma estrutura comum: um nome, atributos e filhos. Comece com uma classe base:
open class Tag(val name: String) {
val attrs = mutableMapOf<String, String>()
val children = mutableListOf<Tag>()
fun render(indent: Int = 0): String {
val pad = " ".repeat(indent)
val attrStr = if (attrs.isEmpty()) "" else " " + attrs.entries.joinToString(" ") { "${it.key}=\"${it.value}\"" }
val body = children.joinToString("\n") { it.render(indent + 2) }
return "$pad<$name$attrStr>\n$body\n$pad</$name>"
}
}Aplicando @DslMarker
Marque todas as classes construtoras para que os escopos internos não possam chamar acidentalmente métodos do escopo externo:
@DslMarker
annotation class HtmlDsl
@HtmlDsl class Html : Tag("html")
@HtmlDsl class Head : Tag("head")
@HtmlDsl class Body : Tag("body")
@HtmlDsl class Div : Tag("div")
@HtmlDsl class P : Tag("p")Adicionando funções construtoras
Cada classe de tag recebe uma função de extensão que cria uma tag filha, aplica o bloco e a adiciona aos filhos:
fun Html.head(block: Head.() -> Unit) = Head().also { it.block(); children += it }
fun Html.body(block: Body.() -> Unit) = Body().also { it.block(); children += it }
fun Body.div(block: Div.() -> Unit) = Div().also { it.block(); children += it }
fun Div.p(text: String) = P().also { it.attrs["text"] = text; children += it }Construtora html de nível superior
Forneça um ponto de entrada de nível superior que crie o elemento raiz e aplique o bloco do usuário:
fun html(block: Html.() -> Unit): Html {
val root = Html()
root.block()
return root
}Usando a DSL
O resultado é uma DSL limpa e legível:
val page = html {
head { }
body {
div {
p("Hello, DSL!")
}
}
}
println(page.render())Adicionando atributos
Adicione um auxiliar attr para definir atributos arbitrários de forma fluida:
fun Tag.attr(key: String, value: String) { attrs[key] = value }
// Usage
div {
attr("class", "container")
p("Content")
}Padrão de DSL de configuração
O mesmo padrão funciona para objetos de configuração. Em vez de tags HTML, use classes do domínio:
class ServerConfig {
var host = "localhost"
var port = 8080
var db: DbConfig? = null
fun database(block: DbConfig.() -> Unit) { db = DbConfig().apply(block) }
}
fun server(block: ServerConfig.() -> Unit) = ServerConfig().apply(block)Aninhando e compondo
Como cada função construtora retorna o objeto criado, você pode compor construtoras livremente. A profundidade de aninhamento da DSL corresponde à profundidade da hierarquia de dados.
Prevenção de erros por meio de tipos
O sistema de tipos impõe a estrutura: você não pode chamar body { } dentro de um Head porque Head não tem uma função de extensão body. Estruturas inválidas são erros em tempo de compilação.
Renderização e testes
Como a DSL produz uma árvore simples de objetos Kotlin, renderizar e testar é fácil: chame render() e verifique a string de saída, ou percorra a árvore de objetos nos testes.
Extensão posterior da DSL
Adicionar novas tags ou opções de configuração exige apenas novas funções de extensão na classe receptora apropriada. Os pontos de chamada existentes não são afetados — aberta/fechada na prática.
Verificação rápida
O que torna uma DSL HTML/de configuração do Kotlin "segura quanto aos tipos"?
Recapitulação: criando uma DSL segura quanto aos tipos
Etapas principais:
- Defina classes construtoras para cada nível da hierarquia
- Use
@DslMarkerpara impedir o vazamento do receptor - Escreva funções de extensão que criem, configurem e adicionem objetos filhos
- Exponha uma função de entrada de nível superior
- A renderização e os testes funcionam com objetos simples do Kotlin
Perguntas Frequentes
A aula “Criando uma DSL de HTML/configuração segura quanto a tipos” é grátis?
Sim — o texto completo de “Criando uma DSL de HTML/configuração segura quanto a tipos” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Kotlin Academy, atualize para CoddyKit PRO. O curso de Kotlin Academy inclui 4 aulas no total.
O que vou aprender em “Criando uma DSL de HTML/configuração segura quanto a tipos”?
Implemente uma DSL simples de configuração usando padrões construtores e lambdas de extensão. Você pratica Kotlin Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar Kotlin Academy?
Nenhuma experiência prévia é necessária. Kotlin Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.
Quanto tempo leva a aula “Criando uma DSL de HTML/configuração segura quanto a tipos”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de Kotlin Academy?
Sim. Cada aula de Kotlin Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Lambda com receptor: a base da DSL
- @DslMarker: impedindo o vazamento de receptores
- Criando uma DSL de HTML/configuração segura quanto a tipos
- Testando e evoluindo DSLs sem interromper os usuários