构建类型安全的 HTML/Config DSL
使用构建器模式和扩展 Lambda 实现简单的配置 DSL。
构建类型安全的 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 反馈 — 无需本地设置。
此课程中的所有课时
- 带接收者的 Lambda:DSL 基础
- @DslMarker:防止接收者泄漏
- 构建类型安全的 HTML/Config DSL
- 在不破坏用户代码的情况下测试和演进 DSL