Bygg en typsäker HTML-/Config-DSL
Implementera en enkel konfigurations-DSL med builder-mönster och extension-lambdor.
Bygg en typsäker HTML-/Config-DSL är en gratis lektion i Kotlin Academy på CoddyKit. Detta är lektion 3 av 4. Du kan läsa vilka 3 lektioner som helst i den här lärvägen kostnadsfritt i sin helhet – därefter låser CoddyKit PRO upp alla lektioner, plus praktisk övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Den ingår i lärvägen för Kotlin Academy, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Kotlin Academy innehåller totalt 4 lektioner.
Mål: en praktisk DSL
Vi ska bygga en minimal men realistisk HTML-builder-DSL i Kotlin. Samma mönster kan användas för konfigurationer, testfixturer och rapportbyggare. Resultatet är ett typsäkert, IDE-vänligt API som ersätter strängkonkatenering.
Definiera basklassen för taggar
Alla HTML-element har samma grundstruktur: ett namn, attribut och underordnade element. Börja med en basklass:
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>"
}
}Tillämpa @DslMarker
Markera alla builder-klasser så att inre omfång inte av misstag kan anropa metoder i yttre omfång:
@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")Lägg till builder-funktioner
Varje taggklass får en extension-funktion som skapar en underordnad tagg, tillämpar blocket och lägger till den bland de underordnade elementen:
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 }html-builder på toppnivå
Ge en startpunkt på toppnivå som skapar rotelementet och tillämpar användarens block:
fun html(block: Html.() -> Unit): Html {
val root = Html()
root.block()
return root
}Använda DSL:en
Resultatet är en ren och lättläst DSL:
val page = html {
head { }
body {
div {
p("Hello, DSL!")
}
}
}
println(page.render())Lägga till attribut
Lägg till en attr-hjälpfunktion för att ange godtyckliga attribut på ett smidigt sätt:
fun Tag.attr(key: String, value: String) { attrs[key] = value }
// Usage
div {
attr("class", "container")
p("Content")
}Konfigurations-DSL-mönstret
Samma mönster fungerar för konfigurationsobjekt. Använd domänklasser i stället för HTML-taggar:
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)Kapsla in och sätt samman
Eftersom varje builder-funktion returnerar det skapade objektet kan du fritt sätta samman builders. DSL:ens kapslingsdjup motsvarar djupet i datahierarkin.
Felprevention med hjälp av typer
Typsystemet tvingar fram strukturen: du kan inte anropa body { } inuti en Head eftersom Head inte har någon body-extension-funktion. Ogiltiga strukturer blir kompileringsfel.
Rendera och testa
Eftersom DSL:en producerar ett vanligt Kotlin-objektträd är rendering och testning enkelt: anropa render() och kontrollera resultatsträngen, eller traversera objektträdet i tester.
Utöka DSL:en senare
Att lägga till nya taggar eller konfigurationsalternativ kräver endast nya extension-funktioner på den lämpliga mottagarklassen. Befintliga anropsställen påverkas inte – öppet/slutet i praktiken.
Snabbkontroll
Vad gör en Kotlin-HTML-/konfigurations-DSL ”typsäker”?
Sammanfattning: Bygga en typsäker DSL
Viktiga steg:
- Definiera builder-klasser för varje nivå i hierarkin
- Använd
@DslMarkerför att förhindra läckage mellan mottagare - Skriv extension-funktioner som skapar, konfigurerar och lägger till underordnade objekt
- Exponera en funktion på toppnivå som startpunkt
- Rendering och testning fungerar med vanliga Kotlin-objekt
Lär dig Kotlin med en AI-lärare – gratis
Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.
- Kurser
- 51
- Lektioner
- 203
Vanliga frågor
Är lektionen ”Bygg en typsäker HTML-/Config-DSL” gratis?
Ja – du kan läsa vilka 3 lektioner som helst i lärvägen Kotlin Academy, inklusive ”Bygg en typsäker HTML-/Config-DSL”, kostnadsfritt i sin helhet här på webben. Därefter låser CoddyKit PRO upp alla lektioner, plus interaktiv övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Kursen i Kotlin Academy innehåller totalt 4 lektioner.
Vad lär jag mig i ”Bygg en typsäker HTML-/Config-DSL”?
Implementera en enkel konfigurations-DSL med builder-mönster och extension-lambdor. Ni övar på Kotlin Academy med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.
Behöver jag någon erfarenhet för att börja lära mig Kotlin Academy?
Du behöver inga förkunskaper. Utbildningen i Kotlin Academy på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 3 av 4.
Hur lång tid tar lektionen ”Bygg en typsäker HTML-/Config-DSL”?
De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.
Kan jag skriva och köra kod i den här Kotlin Academy-lektionen?
Ja. Varje Kotlin Academy-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.
Alla lektioner i den här kursen
- Lambda med receiver: grunden för DSL:er
- @DslMarker: förhindra läckage mellan receivers
- Bygg en typsäker HTML-/Config-DSL
- Testa och utveckla DSL:er utan att bryta användare