4.DSL

/ 0评 / 0

DSL

DSL(Domain Specific Languages),特定领域语言,我们常见的在gradle中依赖管理的配置:

repositories {
    mavenCentral()
}
dependencies {
    compile group: 'org.hibernate', name: 'hibernate-core', version: '3.6.7.Final'
    testCompile group: 'junit', name: 'junit', version: '4.+'
}

这种语法就是DSL,我们可以用kotlin实现这样的功能效果,repositories是一个函数,接收一个lambda,lambda里面又嵌套了另外一个函数.

官方也给出了一个关于DSLhtml例子,我会重新去实现一遍,代码的实现方式可能会略有差异:

fun main(vararg args:String) {

    //函数调用
    fun result() =
        html {
            head {
                title {+"XML encoding with Kotlin"}
            }
            body {
                h1 {+"XML encoding with Kotlin"}
                p  {+"this format can be used as an alternative markup to XML"}

                // an element with attributes and text content
                a(href = "https://kotlinlang.org") {+"Kotlin"}

                // mixed content
                p {
                    +"This is some"
                    b {+"mixed"}
                    +"text. For more see the"
                    a(href = "https://kotlinlang.org") {+"Kotlin"}
                    +"project"
                }

                // content generated by
                p {
                    for (arg in args)
                        +arg
                }
            }
        }
    println(result().render())

代码实现:

abstract class Element(val tag:String){

    val elements = mutableListOf<Element>();

    open fun render():String{
        val sb = StringBuilder()
        sb.append("<${tag}>\n")
        for (element in elements) {
            sb.append("${element.render()}\n")
        }
        sb.append("</${tag}>\n")
        return sb.toString();
    }

    operator fun String.unaryPlus()  = this@Element.elements.add(Text(this))
}

class HTML:Element("html"){

    fun head(init:Head.()->Unit){
        var head = Head()
        head.init()
        elements.add(head)
    }

    fun body(init: Body.() -> Unit){
        var body = Body();
        body.init()
        elements.add(body)
    }

}

class Head:Element("head"){

    fun title(init: Title.() -> Unit){
        val title = Title()
        title.init()
        elements.add(title)
    }

}

class H1:Element("h1"){

}
class P:Element("p"){
    fun b(function: B.() -> Boolean) {
        var b = B()
        b.function()
        elements.add(b)
    }

    fun a(href: String, init: A.() -> Unit) {
        var a = A(href)
        a.init();
        elements.add(a)
    }

}
class A(val href: String):Element("a"){

    override fun render(): String  = "<a href='${href}'>${elements[0].render()}</a>"
}

class B:Element("b");

class Body:Element("body"){
    fun h1(function: H1.() -> Unit) {
        var h1 = H1()
        h1.function()
        elements.add(h1)

    }

    fun p(function: P.() -> Unit) {
        var p = P()
        p.function()
        elements.add(p)
    }

    fun a(href: String, init: A.() -> Unit) {
        var a = A(href)
        a.init();
        elements.add(a)
    }

}

class Title:Element("title");

class Text(val text:String):Element(""){

    override fun render():String = text

}

fun html(init:HTML.()->Unit):HTML{
    var html = HTML()
    html.init()
    return html
}

实现效果:

<html>
<head>
<title>
XML encoding with Kotlin
</title>

</head>

<body>
<h1>
XML encoding with Kotlin
</h1>

<p>
this format can be used as an alternative markup to XML
</p>

<a href='https://kotlinlang.org'>Kotlin</a>
<a href='https://kotlinlang.org'>Kotlin</a>
<p>
This is some
<b>
mixed
</b>

text. For more see the
project
</p>

<p>
</p>

</body>

</html>

格式化没有去处理,可能会有点丑陋哈,代码不算复杂,html是一个高阶函数,在html参数函数里面调用了headbody函数,为啥可以直接写成headbody呢?其实是少写了个this,因为html函数的参数是HTML类的拓展函数,所以headbody都应该是HTML类的函数,那样才可以直接head{},body{}来调用,理清楚这点后面就很好理解.

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注