天天看点

Kotlin领域特定语言(DSL)

一、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>           

好啦,结束啦!