一、DSL的概念
只在特定领域内使用的语言
例如:
—HTML、Gradle、SQL等等
特点:
计算机编程语言
具有语言的表达能力
有限的表达能力
关注某个特定的领域
二、下面用DSL来写一个例子吧
需要下面五个类:
三、创建一个Node节点的接口
package cn.kotliner.kotlin
/**
* @author:wangdong
* @description:1.Node节点的接口
*/
interface Node {
//每个节点都应该有一个render方法
//例如对于HTML标签,它的render方法应该输出什么
fun render(): String
}
四、写一个通用的Tag类,实现这个接口
package cn.kotliner.kotlin
/**
* @author:wangdong
* @description:2.通用的tag类
*/
/***
* 实现了Node接口
* Tag类需要Open,以便其他类继承
*/
open class Tag(val name: String): Node{
//1.定义一个Node节点的list
val children = ArrayList<Node>()
//定一个表现属性的Map
val properties = HashMap<String,String>()
//定义一个String的扩展方法,参数是String
operator fun String.invoke(value: String){
properties[this] = value
}
//定义一个String加法的拓展
operator fun String.unaryPlus(){
children.add(StringNode(this))
}
//定义一个添加运算符
operator fun plus(node: Node){
children.add(node)
}
//定义一个String的扩展方法,参数是Tag.()
operator fun String.invoke(block: Tag.() -> Unit){
children.add(Tag(this).apply(block))
}
//例如<html id = "htmlId" style = ""><head></head><body></body></html>
// <html id="htmlId" style=""><head> </head> <body></body></html>
override fun render(): String {
return StringBuilder()
.append("<")
.append(name)
.let {
stringBuilder ->
if(!this.properties.isEmpty()){
stringBuilder.append(" ")
this.properties.forEach{
stringBuilder.append(it.key)
.append("=\"")
.append(it.value)
.append("\" ")
}
}
stringBuilder
}
.append(">")
.let {
stringBuilder ->
children.map(Node::render).map(stringBuilder::append)
stringBuilder
}
.append("</$name>")
.toString()
}
}
五、写一个主函数
package cn.kotliner.kotlin
/**
* @author:wangdong
* @description:3.写一个主函数
*/
fun main(args: Array<String>) {
//定义一个tag
//给它添加一个属性
/*Tag("html").apply {
properties["id"] = "Htmlid"
//给节点添加一个tag
children.add(Tag("head"))
}.render().let(::println)*/
//此时代码可以改一下了
html{
"id"("HtmlId")
//给节点添加一个tag
//children.add(Tag("head"))
"head"{
"id"("headId")
}
body{
id = "bodyId"
`class` = "bodyClass"
"a"{
"href"("https://www.kotliner.cn")
+ "Kotlin中文博客"
}
}
"div"{}
}.render().let(::println)
}
六、写一个Nodes,用来写各种方法
package cn.kotliner.kotlin
/**
* @author:wangdong
* @description:4.定一个Nodes
*/
fun html(block: Tag.() -> Unit): Tag{
//首先new了一个Tag,名字叫html,用它的作用域去调用了一下block,传的this就是Tag("html")
return Tag("html").apply(block)
}
/**
* head的拓展方法
* 要是tag的扩展方法
* 不需要返回值
*/
fun Tag.head(block: Head.() -> Unit){
this@head + Head().apply(block)
}
/**
* body的拓展方法
*/
fun Tag.body(block: Body.() -> Unit){
this@body + Body().apply(block)
}
/**
* 定义一个节点
* 参数content
* 返回值:Node
*/
class StringNode(val content: String):Node{
override fun render() = content
}
/**定义头*/
class Head: Tag("head")
/**定义体*/
class Body: Tag("body"){
var id by MapDelegate(properties)
var `class` by MapDelegate(properties)
}
七、定义一个MapDelegate用来Set或者Get值
package cn.kotliner.kotlin
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
/**
* @author:wangdong
* @description:5.定义一个mapDelegate
*/
/**
* map是可读写的
* 实现一个接口
*/
class MapDelegate(val map: MutableMap<String,String>):ReadWriteProperty<Any,String>{
/**
* Returns the value of the property for the given object.
* @param thisRef the object for which the value is requested.
* @param property the metadata for the property.
* @return the property value.
* 返回值
*/
override fun getValue(thisRef: Any, property: KProperty<*>): String {
return map[property.name] ?: ""
}
/**
* Sets the value of the property for the given object.
* @param thisRef the object for which the value is requested.
* @param property the metadata for the property.
* @param value the value to set.
* 设置值
*/
override fun setValue(thisRef: Any, property: KProperty<*>, value: String) {
map[property.name] = value
}
}
八、运行一下主方法
得到的输出结果:
<html id="HtmlId" ><head id="headId" ></head><body id="bodyId" class="bodyClass" ><a href="https://www.kotliner.cn" >Kotlin中文博客</a></body></html>
好啦,结束啦!