当前位置:首页 > Web开发 > 正文

负责输出本标签内容(包含标签名

2024-03-31 Web开发

首先是一些根本常识, 包孕什么是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的例子.
后面就对这个例子进行一个梳理和解析.

html实例解析 1 需求分析

首先明确一下我们的方针.

做一个最简单的假设, 我们等候的功效是在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>

文字元素是作为标签的一个子标签的.
这里的实现不容易本身想到, 直接看后面的实现部分揭晓答案吧.

3 实现

有了前面的心路历程, 再来看实现就能容易一些.

基类实现

首先是最根基的接口, 只包罗了衬着要领:

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