负责输出本标签内容(包含标签名
首先是一些根本常识, 包孕什么是DSL, 实现DSL操作了那些Kotlin的语法, 常用的情形和风行的库.
对html实例的解析, 没有一冲上来就展示正确答案, 而是凭据分析需求, 设计, 和实现细化的法式来逐步让解决方案变得明朗清晰.
理论根本 DSL: 范围特定语言DSL: Domain Specific Language.
专注于一个方面而特殊设计的语言.
可以看做是封装了一套对象, 用于特定的成果, 优势是复用性和可读性的增强. -> 意思是提取了一套库吗?
不是.
DSL和简单的要领提取差别, 有可能代码的形式或者语法变了, 更接近自然语言, 更容易让人看懂.
Kotlin语言根本做一个DSL, 转变语法, 在Kotlin中主要依靠:
lambda表达式.
扩展要领.
三个lambda语法:
如果只有一个参数, 可以用it直接暗示.
如果lambda表达式是函数的最后一个参数, 可以移到小括号()外面. 如果lambda是独一的参数, 可以省略小括号().
lambda可以带receiver.
扩展要领.
风行的DSL使用场景Gradle的build文件就是用DSL写的.
之前是Groovy DSL, 此刻也有Kotlin DSL了.
还有Anko.
这个库包罗了很多成果, UI组件, 网络, 后台任务, 数据库等.
和处事器端用的: Ktor
应用场景: Type-Safe Builders
type-safe builders指类型安适, 静态类型的builders.
这种builders就对照适合创建Kotlin DSL, 用于构建庞大的层级布局数据, 用半陈说式的方法.
官方文档举的是html的例子.
后面就对这个例子进行一个梳理和解析.
首先明确一下我们的方针.
做一个最简单的假设, 我们等候的功效是在Kotlin代码中类似这样写:
html { head { } body { } }就能输出这样的文本:
<html> <head> </head> <body> </body> </html> 发明1: 挪用形式仔细不雅察看第一段Kotlin代码, html{}应该是一个要领挪用, 只不过这个要领只有一个lambda表达式作为参数, 所以省略了().
里面的head{}和body{}也是同理, 都是两个以lambda作为独一参数的要领.
发明2: 层级关系因为标签的层级关系, 可以理解为每个标签都卖力本身包罗的内容, 父标签只卖力按挨次显示子标签的内容.
发明3: 挪用限制由于<head>和<body>等标签只在<html>标签中才有意义, 所以应该限制外部只能挪用html{}要领, head{}和body{}要领只有在html{}的要领体中才华挪用.
发明4: 应该需要完成的如何插手和显示文字.
标签可能有本身的属性.
标签应该有正确的缩进.
2 设计 标签基类因为标签看起来都是类似的, 为了代码复用, 首先设计一个抽象的标签类Tag, 包罗:
标签名称.
一个子标签的list.
一个属性列表.
一个衬着要领, 卖力输出本标签内容(包罗标签名, 子标签和所有属性).
怎么加文字文字对照特殊, 它不带标签标记<>, 就输出本身.
所以它的衬着要领就是输出文字自己.
可以提取出一个越发基类的接口Element, 只包罗衬着要领. 这个接口的子类是Tag和TextElement.
有文字的标签, 如<title>, 它的输出功效:
<title> HTML encoding with Kotlin </title>文字元素是作为标签的一个子标签的.
这里的实现不容易本身想到, 直接看后面的实现部分揭晓答案吧.
有了前面的心路历程, 再来看实现就能容易一些.
基类实现首先是最根基的接口, 只包罗了衬着要领:
interface Element { fun render(builder: StringBuilder, indent: String) }它的直接子类标签类:
abstract class Tag(val name: String) : Element { val children = arrayListOf<Element>() val attributes = hashMapOf<String, String>() protected fun <T : Element> initTag(tag: T, init: T.() -> Unit): T { tag.init() children.add(tag) return tag } override fun render(builder: StringBuilder, indent: String) { builder.append("$indent<$name${renderAttributes()}>\n") for (c in children) { c.render(builder, indent + " ") } builder.append("$indent</$name>\n") } private fun renderAttributes(): String { val builder = StringBuilder() for ((attr, value) in attributes) { builder.append(" $attr=\"$value\"") } return builder.toString() } override fun toString(): String { val builder = StringBuilder() render(builder, "") return builder.toString() } }完成了自身标签名和属性的衬着, 接着遍历子标签衬着其内容. 注意这里为所有子标签加上了一层缩进.
initTag()这个要领是protected的, 供子类挪用, 为本身加上子标签.
带文字的标签带文字的标签有个抽象的基类:
abstract class TagWithText(name: String) : Tag(name) { operator fun String.unaryPlus() { children.add(TextElement(this)) } }这是一个对+运算符的重载, 这个扩展要领把字符串包装成TextElement类东西, 然后加到当前标签的子标签中去.
温馨提示: 本文由Jm博客推荐,转载请保留链接: https://www.jmwww.net/file/web/30529.html