《Kotlin 程序設(shè)計(jì)》第十一章 Kotlin實(shí)現(xiàn)DSL

第十一章 Kotlin實(shí)現(xiàn)DSL

DSL

DSL 即 domain-specific languages,領(lǐng)域特定語言珊随。和一般的編程語言不同献宫,領(lǐng)域特定語言只能用于特定的領(lǐng)域中并且表現(xiàn)形式有限。領(lǐng)域特定語言最大的功能就是可以讓語言本身更容易閱讀绑改,方便開發(fā)者和領(lǐng)域?qū)<疫M(jìn)行交流盯另。

實(shí)現(xiàn) DSL

Java 中 DSL 的最簡(jiǎn)單實(shí)現(xiàn)方式就是構(gòu)造器模式性含,而在 Kotlin 過去的版本中可以省略 .,所以可以寫成更易讀的代碼鸳惯,但是現(xiàn)在的版本已經(jīng)不支持了胶滋。

構(gòu)造器模式

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'"
}

}
構(gòu)建對(duì)象

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

使用閉包構(gòu)建 DSL

Kotlin 像 Groovy 一樣也能通過閉包構(gòu)建 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
}
調(diào)用 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 {
    //遍歷數(shù)據(jù)
    for ((num, string) in data) {
        //創(chuàng)建 HTML 標(biāo)簽的函數(shù)
        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() { +"Kotlin" }

                    // mixed content
                    p {
                        +"This is some"
                        b { +"mixed" }
                        +"text. For more see the"
                        a() { +"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 開發(fā)者社區(qū)

國內(nèi)第一Kotlin 開發(fā)者社區(qū)公眾號(hào),主要分享后德、交流 Kotlin 編程語言部宿、Spring Boot、Android瓢湃、React.js/Node.js理张、函數(shù)式編程、編程思想等相關(guān)主題绵患。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末雾叭,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子落蝙,更是在濱河造成了極大的恐慌织狐,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件筏勒,死亡現(xiàn)場(chǎng)離奇詭異移迫,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)管行,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,732評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門厨埋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人捐顷,你說我怎么就攤上這事荡陷∮晷В” “怎么了?”我有些...
    開封第一講書人閱讀 163,711評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵废赞,是天一觀的道長设易。 經(jīng)常有香客問我,道長蛹头,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評(píng)論 1 293
  • 正文 為了忘掉前任戏溺,我火速辦了婚禮渣蜗,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘旷祸。我一直安慰自己耕拷,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,432評(píng)論 6 392
  • 文/花漫 我一把揭開白布托享。 她就那樣靜靜地躺著骚烧,像睡著了一般。 火紅的嫁衣襯著肌膚如雪闰围。 梳的紋絲不亂的頭發(fā)上赃绊,一...
    開封第一講書人閱讀 51,301評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音羡榴,去河邊找鬼碧查。 笑死,一個(gè)胖子當(dāng)著我的面吹牛校仑,可吹牛的內(nèi)容都是我干的忠售。 我是一名探鬼主播,決...
    沈念sama閱讀 40,145評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼迄沫,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼稻扬!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起羊瘩,我...
    開封第一講書人閱讀 39,008評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤泰佳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后尘吗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體乐纸,經(jīng)...
    沈念sama閱讀 45,443評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,649評(píng)論 3 334
  • 正文 我和宋清朗相戀三年摇予,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了汽绢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,795評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡侧戴,死狀恐怖宁昭,靈堂內(nèi)的尸體忽然破棺而出跌宛,到底是詐尸還是另有隱情,我是刑警寧澤积仗,帶...
    沈念sama閱讀 35,501評(píng)論 5 345
  • 正文 年R本政府宣布疆拘,位于F島的核電站,受9級(jí)特大地震影響寂曹,放射性物質(zhì)發(fā)生泄漏哎迄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,119評(píng)論 3 328
  • 文/蒙蒙 一隆圆、第九天 我趴在偏房一處隱蔽的房頂上張望漱挚。 院中可真熱鬧,春花似錦渺氧、人聲如沸旨涝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽白华。三九已至,卻和暖如春贩耐,著一層夾襖步出監(jiān)牢的瞬間弧腥,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評(píng)論 1 269
  • 我被黑心中介騙來泰國打工潮太, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鸟赫,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,899評(píng)論 2 370
  • 正文 我出身青樓消别,卻偏偏與公主長得像抛蚤,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子寻狂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,724評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容