面试题答案
一键面试1. 定义HTML标签类
abstract class Tag(val name: String) {
val children = mutableListOf<Tag>()
val attributes = mutableMapOf<String, String>()
override fun toString(): String {
val attributeString = attributes.entries.joinToString(" ") { "${it.key}=\"${it.value}\"" }
val childrenString = children.joinToString("")
return "<$name ${if (attributeString.isNotEmpty()) attributeString else ""}>$childrenString</$name>"
}
}
class TextTag(val text: String) : Tag("") {
override fun toString(): String = text
}
这里定义了一个Tag
抽象类,用于表示HTML标签,包含标签名name
、子标签children
和属性attributes
。还定义了一个TextTag
类,用于表示文本内容,它继承自Tag
,但标签名为空。
2. 构建器定义
class HtmlBuilder {
private var root: Tag? = null
private val stack = mutableListOf<Tag>()
fun tag(name: String, init: Tag.() -> Unit): HtmlBuilder {
val tag = Tag(name)
if (stack.isEmpty()) {
root = tag
} else {
stack.last().children.add(tag)
}
stack.add(tag)
tag.init()
stack.removeLast()
return this
}
fun text(text: String) {
val textTag = TextTag(text)
if (stack.isNotEmpty()) {
stack.last().children.add(textTag)
}
}
override fun toString(): String = root?.toString() ?: ""
}
HtmlBuilder
类用于构建HTML结构。tag
方法用于创建一个新的标签,并将其添加到当前父标签(如果有)的子标签列表中。text
方法用于添加文本内容到当前父标签。
3. 使用构建器的DSL风格代码
fun buildHtml(block: HtmlBuilder.() -> Unit): String {
val builder = HtmlBuilder()
builder.block()
return builder.toString()
}
fun main() {
val html = buildHtml {
tag("html") {
tag("head") {
tag("title") {
text("My Page")
}
}
tag("body") {
tag("h1") {
text("Welcome!")
}
tag("p", {
attributes["class"] = "content"
text("This is some content.")
})
}
}
}
println(html)
}
buildHtml
函数接收一个HtmlBuilder
的扩展函数作为参数,通过这种方式可以以DSL风格构建HTML。在main
函数中展示了具体的使用方式,确保了标签的嵌套和属性设置符合HTML语法规则。
原理解释
- 类型系统保证安全:通过定义
Tag
及其相关子类,Kotlin的类型系统确保只有合法的标签和文本可以被添加到相应位置。例如,只能将Tag
或TextTag
类型的对象添加到Tag
的children
列表中。 - 构建器模式:
HtmlBuilder
类采用构建器模式,通过tag
和text
方法逐步构建复杂的HTML结构。stack
用于跟踪当前的父标签,保证标签的正确嵌套。 - DSL风格:通过扩展函数和Lambda表达式,使得构建HTML结构的代码看起来更像DSL,提高了代码的可读性和易用性。例如,在
buildHtml
函数中,可以以一种自然的方式描述HTML标签的层次结构和属性设置。