Kotlin Academy · 课时

构建类型安全的 HTML/Config DSL

使用构建器模式和扩展 Lambda 实现简单的配置 DSL。

第 3 / 4 课14 个步骤

构建类型安全的 HTML/Config DSL 是 CoddyKit 上的免费 Kotlin Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Kotlin Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Kotlin Academy 课程共包含 4 节课。

目标:构建实用的 DSL

我们将使用 Kotlin 构建一个简洁但贴近实际的 HTML 构建器 DSL。同样的模式也适用于任何配置、测试固定装置或报告构建器。最终得到的是一个类型安全、对 IDE 友好的 API,可以替代字符串拼接。

定义 Tag 基类

所有 HTML 元素都具有相同的基本结构:名称、属性和子元素。先从基类开始:

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>"
    }
}

应用 @DslMarker

标记所有构建器类,使内部作用域无法意外调用外部作用域方法:

@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")

添加构建器函数

每个标签类都获得一个扩展函数,用于创建子标签、应用代码块,并将其添加到子元素中:

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 构建器

提供一个顶层入口,创建根元素并应用用户代码块:

fun html(block: Html.() -> Unit): Html {
    val root = Html()
    root.block()
    return root
}

使用 DSL

结果是一个简洁易读的 DSL:

val page = html {
    head { }
    body {
        div {
            p("Hello, DSL!")
        }
    }
}
println(page.render())

添加属性

添加一个 attr 辅助函数,以流畅的方式设置任意属性:

fun Tag.attr(key: String, value: String) { attrs[key] = value }

// Usage
div {
    attr("class", "container")
    p("Content")
}

配置 DSL 模式

同样的模式也适用于配置对象。此时不使用 HTML 标签,而是使用领域类:

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)

嵌套与组合

由于每个构建器函数都会返回所创建的对象,因此您可以自由组合构建器。DSL 的嵌套深度与数据层次结构的深度相匹配。

通过类型防止错误

类型系统会强制实施结构约束:您无法在 Head 中调用 body { },因为 Head 没有 body 扩展函数。无效结构会在编译时报告错误。

渲染与测试

由于 DSL 生成的是普通 Kotlin 对象树,因此渲染和测试都很简单:调用 render() 并断言输出字符串,或者在测试中遍历对象树。

后续扩展 DSL

添加新标签或配置选项只需要在适当的接收者类中新增扩展函数。现有调用位置不受影响——这在实践中体现了开闭原则。

快速检查

是什么让 Kotlin HTML/配置 DSL 具备“类型安全”特性?

回顾:构建类型安全的 DSL

关键步骤:

  • 为层次结构的每一级定义构建器类
  • 使用 @DslMarker 注解,防止接收者泄漏
  • 编写用于创建、配置和添加子对象的扩展函数
  • 公开一个顶层入口函数
  • 渲染和测试都基于普通 Kotlin 对象进行
免费开始

用 AI 导师学习 Kotlin — 免费

在浏览器中编写并运行真实代码,获得全天候 AI 导师的即时帮助,并在网页或应用中继续学习。

课程
51
课程
203

常见问题解答

「构建类型安全的 HTML/Config DSL」课时是免费的吗?

是的 — 「构建类型安全的 HTML/Config DSL」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Kotlin Academy 课程的其余内容,请升级到 CoddyKit PRO。 Kotlin Academy 课程共包含 4 节课。

「构建类型安全的 HTML/Config DSL」这节课中我会学到什么?

使用构建器模式和扩展 Lambda 实现简单的配置 DSL。 你通过在浏览器中直接运行的动手代码来练习 Kotlin Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Kotlin Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Kotlin Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「构建类型安全的 HTML/Config DSL」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Kotlin Academy 课中编写并运行代码吗?

能。每节 Kotlin Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. 带接收者的 Lambda:DSL 基础
  2. @DslMarker:防止接收者泄漏
  3. 构建类型安全的 HTML/Config DSL
  4. 在不破坏用户代码的情况下测试和演进 DSL
← 返回 Kotlin Academy