美文网首页Kotlin 程序设计Kotlin程序设计
《Kotlin 程序设计》第十一章 Kotlin实现DSL

《Kotlin 程序设计》第十一章 Kotlin实现DSL

作者: 光剑书架上的书 | 来源:发表于2017-05-29 03:57 被阅读166次

    第十一章 Kotlin实现DSL

    正式上架:《Kotlin极简教程》Official on shelves: Kotlin Programming minimalist tutorial
    京东JD:https://item.jd.com/12181725.html
    天猫Tmall:https://detail.tmall.com/item.htm?id=558540170670

    DSL

    DSL 即 domain-specific languages,领域特定语言。和一般的编程语言不同,领域特定语言只能用于特定的领域中并且表现形式有限。领域特定语言最大的功能就是可以让语言本身更容易阅读,方便开发者和领域专家进行交流。

    实现 DSL

    Java 中 DSL 的最简单实现方式就是构造器模式,而在 Kotlin 过去的版本中可以省略 .,所以可以写成更易读的代码,但是现在的版本已经不支持了。

    构造器模式

    Machine machine = new Machine.Builder()
    .setCore(8)
    .setArch("64 bits")
    .setOs("Linux")
    .build();
    DSL 方式

    定义必要的类和方法

    data class Cpu(val core: Int, val arch: String)

    class Machine {
    var cpu: Cpu? = null
    var os: String? = null

    fun having(cores: Int, arch: String): Machine {
        cpu = Cpu(cores, arch)
        return this
    }
    
    fun os(os: String): Machine {
        this.os = os
        return this
    }
    
    override fun toString(): String {
        return "Machine{cpu=$cpu, os='$os'"
    }
    

    }
    构建对象

    val m1 = Machine().having(8, "64 bits").os("linux")
    val m2 = Machine().having(4, "32 bits").os("Windows")
    可以看到使用 DSL 后代码更加易读。

    使用闭包构建 DSL

    Kotlin 像 Groovy 一样也能通过闭包构建 DSL,语法看起来很像 Groovy。

    定义必要的类和方法

    class EmailSpec {
    fun from(from: String) = println("From: $from")
    fun to(to: String) = println("To: $to")
    fun subject(subject: String) = println("Subject: $subject")
    fun body(init: BodySpec.() -> Unit): BodySpec {
    val body = BodySpec()
    body.init()
    return body
    }
    }

    class BodySpec {
    fun p(p: String) = println("P: $p")
    }

    fun email(init: EmailSpec.() -> Unit): EmailSpec {
    val email = EmailSpec()
    email.init()
    return email
    }
    调用 DSL 语句

    email {
    from ("dsl-guru@mycompany.com")
    to ("john.doe@waitaminute.com")
    subject ("The pope has resigned!")
    body {
    p ("Really, the pope has resigned!")
    }
    }

    val data = mapOf(1 to "one", 2 to "two")
    
    createHTML().table {
        //遍历数据
        for ((num, string) in data) {
            //创建 HTML 标签的函数
            tr {
               td { +"$num" } 
               td { +string }
            }
        }
    }
    
    /**
     * This is an example of a Type-Safe Groovy-style Builder
     *
     * Builders are good for declaratively describing data in your code.
     * In this example we show how to describe an HTML page in Kotlin.
     *
     * See this page for details:
     * http://kotlinlang.org/docs/reference/type-safe-builders.html
     */
    package html
    
    fun main(args: Array<String>) {
        val 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 = "http://jetbrains.com/kotlin") { +"Kotlin" }
    
                        // mixed content
                        p {
                            +"This is some"
                            b { +"mixed" }
                            +"text. For more see the"
                            a(href = "http://jetbrains.com/kotlin") { +"Kotlin" }
                            +"project"
                        }
                        p { +"some text" }
    
                        // content generated from command-line arguments
                        p {
                            +"Command line arguments were:"
                            ul {
                                for (arg in args)
                                    li { +arg }
                }
                        }
                    }
                }
        println(result)
    }
    
    interface Element {
        fun render(builder: StringBuilder, indent: String)
    }
    
    class TextElement(val text: String) : Element {
        override fun render(builder: StringBuilder, indent: String) {
            builder.append("$indent$text\n")
        }
    }
    
    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 (a in attributes.keys) {
                builder.append(" $a=\"${attributes[a]}\"")
        }
            return builder.toString()
        }
    
    
        override fun toString(): String {
            val builder = StringBuilder()
            render(builder, "")
            return builder.toString()
        }
    }
    
    abstract class TagWithText(name: String) : Tag(name) {
        operator fun String.unaryPlus() {
            children.add(TextElement(this))
        }
    }
    
    class HTML() : TagWithText("html") {
        fun head(init: Head.() -> Unit) = initTag(Head(), init)
    
        fun body(init: Body.() -> Unit) = initTag(Body(), init)
    }
    
    class Head() : TagWithText("head") {
        fun title(init: Title.() -> Unit) = initTag(Title(), init)
    }
    
    class Title() : TagWithText("title")
    
    abstract class BodyTag(name: String) : TagWithText(name) {
        fun b(init: B.() -> Unit) = initTag(B(), init)
        fun p(init: P.() -> Unit) = initTag(P(), init)
        fun h1(init: H1.() -> Unit) = initTag(H1(), init)
        fun ul(init: UL.() -> Unit) = initTag(UL(), init)
        fun a(href: String, init: A.() -> Unit) {
            val a = initTag(A(), init)
            a.href = href
        }
    }
    
    class Body() : BodyTag("body")
    class UL() : BodyTag("ul") {
        fun li(init: LI.() -> Unit) = initTag(LI(), init)
    }
    
    class B() : BodyTag("b")
    class LI() : BodyTag("li")
    class P() : BodyTag("p")
    class H1() : BodyTag("h1")
    
    class A() : BodyTag("a") {
        public var href: String
            get() = attributes["href"]!!
            set(value) {
                attributes["href"] = value
            }
    }
    
    fun html(init: HTML.() -> Unit): HTML {
        val html = HTML()
        html.init()
        return html
    }
    
    

    https://www.kotliner.cn/2017/05/15/2017-5-11-KotlinDSL2/

    相关文章

      网友评论

        本文标题:《Kotlin 程序设计》第十一章 Kotlin实现DSL

        本文链接:https://www.haomeiwen.com/subject/fclkfxtx.html