閱讀805 返回首頁    go 阿裏雲 go 技術社區[雲棲]


《Kotin 極簡教程》第14章 使用 Kotlin DSL

第14章 使用 Kotlin DSL

最新上架!!!《 Kotlin極簡教程》 陳光劍 (機械工業出版社)
可直接打開京東,淘寶,當當===> 搜索: Kotlin 極簡教程
https://www.jianshu.com/p/35b487734339

我們在前麵的章節中,已經看到了 Kotlin DSL 的強大功能。例如Gradle 的配置文件 build.gradle (Groovy),以及前麵我們涉及到的Gradle Script Kotlin(Kotlin)、Anko(Kotlin)等,都是 DSL。我們可以看出,使用DSL的編程風格,可以讓程序更加簡單幹淨、直觀簡潔。當然,我們也可以創建自己的 DSL。

本章就讓我們一起來學習一下 Kotlin中 DSL的相關內容。

我們在上一章中已經看到了在 Android 中使用下麵這樣的 嵌套DSL 風格的代碼來替代 XML 式風格的視圖文件

        UI {
            // AnkoContext

            verticalLayout {
                padding = dip(30)
                var title = editText {
                    // editText 視圖
                    id = R.id.todo_title
                    hintResource = R.string.title_hint
                }

                var content = editText {
                    id = R.id.todo_content
                    height = 400
                    hintResource = R.string.content_hint
                }
                button {
                    // button 視圖
                    id = R.id.todo_add
                    textResource = R.string.add_todo
                    textColor = Color.WHITE
                    setBackgroundColor(Color.DKGRAY)
                    onClick { _ -> createTodoFrom(title, content) }
                }
            }
        }

相比 XML 風格的 DSL(XML 本質上講也是一種 DSL),明顯使用原生的編程語言(例如Kotlin)DSL 風格更加簡單幹淨,也更加自由靈活。

Kotlin DSL 的編程風格是怎樣的呢?以及其背後實現的原理是怎樣的呢?下麵就讓我一起來探討一下。

DSL 是什麼

DSL(Domain-Specific Language,領域特定語言)指的是專注於特定問題領域的計算機語言(領域專用語言)。不同於通用的計算機語言(GPL),領域特定語言隻用在某些特定的領域。

“領域”可能特指某一產業,比如保險、教育、航空、醫療等;它也可能是指一種方法或者技術,比如JavaEE、.NET、數據庫、服務、消息、架構以及領域驅動設計。開發DSL語言的目的在於,我們要以一種更優雅、更簡潔的方式麵對領域中的一些列挑戰。之所以能夠這樣,是因為這個語言剛好夠用於解決領域中唯一存在的一係列挑戰,一點兒不多、也一點兒不少,剛剛好。

比如用來顯示網頁的HTML語言。更加典型的例子是Gradle,它基於Ant 和 Maven,使用基於Groovy的DSL 來聲明項目構建配置 build.gradle,而不是傳統的XML。

DSL 簡單講就是對一個特定問題 (受限的表達能力) 的方案模型的更高層次的抽象表達(領域語言),使其更加簡單易懂 (容易理解的語義以及清晰的語義模型)。

DSL 隻是問題解決方案模型的外部封裝,這個模型可能是一個 API 庫,也可能是一個完整的框架等等。DSL 提供了思考特定領域問題的模型語言,這使得我們可以更加簡單高效地來解決問題。DSL 聚焦一個特定的領域,簡單易懂,功能極簡但完備。DSL 讓我們理解和使用模型更加簡易。

DSL 有內部 DSL 跟外部 DSL 之分。例如 Gradle、Anko 等都是我們使用通用編程語言(Java 和 Kotlin)創建的內部DSL。

內部DSL

內部DSL是指與項目中使用的通用目的編程語言(Java、C#或Ruby)緊密相關的一類DSL。它基於通用編程語言實現。

例如,Rails框架被稱為基於Ruby的DSL,用於管理Ruby開發的Web應用程序。Rails之所以被稱為DSL,原因之一在於Rails應用了一些Ruby語言的特性,使得基於Rails編程看上去與基於通用目的的Ruby語言編程並不相同。

根據Martin Fowler和Eric Evans的觀點,框架或者程序庫的API是否滿足內部DSL的關鍵特征之一就是它是否有一個流暢(fluent)的接口。這樣,你就能夠用短小的對象表達式去組織一個原本很長的表達式,使它讀起來更加自然。

外部DSL

外部DSL跟通用編程語言(GPL)類似,但是外部DSL更加專注於特定領域。

創建外部DSL和創建一種通用的編程語言的過程是相似的,它可以是編譯型或者解釋型的。它具有形式化的文法,隻允許使用良好定義的關鍵字和表達式類型。經過編譯的DSL通常不會直接產生可執行的程序(但是它確實可以)。

大多數情況下,外部DSL可以轉換為一種與核心應用程序的操作環境相兼容的資源,也可以轉換為用於構建核心應用的通用目的編程語言。例如,Hibernate中使用的對象-關係映射文件,就是由外部DSL轉換為資源的實例。

提示:關於 DSL 的詳細介紹可以參考:《領域特定語言》(Martin Fowler)這本書。

Kotlin 的 DSL 特性支持

許多現代語言為創建內部 DSL 提供了一些先進的方法, Kotlin 也不例外。

在Kotlin 中創建 DSL , 一般主要使用下麵兩個特性:

  • 擴展函數、擴展屬性
  • 帶接收者的 Lambda 表達式(高階函數)

例如上麵的示例的 UI {...} 的代碼,我們舉例簡單說明如下

函數名 函數簽名 備注說明
UI fun Fragment.UI(init: AnkoContext.() -> Unit):AnkoContext android.support.v4.app.Fragment的擴展函數; 入參 init 是一個帶接收者的函數字麵值, 我們直接傳入的是一個 Lambda 表達式
verticalLayout inline fun ViewManager.verticalLayout(init: _LinearLayout.() -> Unit): LinearLayout android.view.ViewManager的擴展函數

使用kotlinx.html DSL 寫前端代碼

為了加深對 Kotlin DSL 實用性上的理解,我們本節再介紹一個 Kotlin 中關於
HTML 的 DSL: kotlinx.html 。

kotlinx.html 是可在 Web 應用程序中用於構建 HTML 的 DSL。 它可以作為傳統模板係統(例如JSP、FreeMarker等)的替代品。

kotlinx. html 分別提供了kotlinx-html-jvm 和 kotlinx-html-js庫的DSL , 用於在 JVM 和瀏覽器 (或其他 javascript 引擎) 中直接使用 Kotlin 代碼來構建 html, 直接解放了原有的 HTML 標簽式的前端代碼。這樣,我們 也可以使用 Kotlin來先傳統意義上的 HTML 頁麵了。 Kotlin Web 編程將會更加簡單純淨。

提示: 更多關於kotlinx.html的相關內容可以參考它的 Github 地址 :https://github.com/Kotlin/kotlinx.html

要使用 kotlinx.html 首先添加依賴

dependencies {
    def kotlinx_html_version = "0.6.3"
    compile "org.jetbrains.kotlinx:kotlinx-html-jvm:${kotlinx_html_version}"
    compile "org.jetbrains.kotlinx:kotlinx-html-js:${kotlinx_html_version}"
    ...
}

kotlinx.html 最新版本發布在 https://jcenter.bintray.com/ 倉庫上,所以我們添加一下倉庫的配置

repositories {
    maven { url 'https://jitpack.io' }
    mavenCentral()
    jcenter() // https://jcenter.bintray.com/ 倉庫
    maven { url "https://repo.spring.io/snapshot" }
    maven { url "https://repo.spring.io/milestone" }
}

我們來寫一個極簡百度首頁示例。這個頁麵的前端 HTML 代碼如下:

<!DOCTYPE html>
<html lang=zh-CN>
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name=viewport content="width=device-width,initial-scale=1">
    <title>百度一下</title>
    <link href="https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
    <link href="dsl.css" rel="stylesheet">
    <script src="dsl.js"></script>
</head>
<body>
<div >
    <div >
        ![](https://upload-images.jianshu.io/upload_images/1233356-49a0fecdc8bfa9cf.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    </div>

    <form >
        <input  >
        <button  type="submit" >百度一下</button>
    </form>
</div>
</body>
</html>

其中,dsl.css文件內容如下

.ipad {
    margin: 10px
}

.center {
    text-align: center;
}

dsl.js 文件內容如下

$(function () {
    $('#baiduBtn').on('click', function () {
        var wd = $('#wd').val()
        window.open("https://www.baidu.com/s?wd=" + wd)
    })
})

上麵我們是通常使用的 HTML+JS+CSS 的方式來寫前端頁麵的方法。現在我們把 HTML 部分的代碼用Kotlin 的 DSL kotlinx.html 來重新實現一遍。

我們首先新建 Kotlin + Spring Boot 工程,然後直接來寫 Kotlin 視圖類HelloDSLView,代碼如下:

package com.easy.kotlin.chapter14_kotlin_dsl.view

import kotlinx.html.*
import kotlinx.html.stream.createHTML
import org.springframework.stereotype.Service

/**
 * Created by jack on 2017/7/22.
 */
@Service
class HelloDSLView {
    fun html(): String {
        return createHTML().html {
            head {
                meta {
                    charset = "utf-8"
                    httpEquiv = "X-UA-Compatible"
                    content = "IE=edge"
                }
                title("百度一下")
                link {
                    href = "https://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css"
                    rel = "stylesheet"
                }
                script {
                    src = "https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"
                }
                link {
                    href = "dsl.css"
                    rel = "stylesheet"
                }
                script {
                    src = "dsl.js"
                }
            }

            body {
                div(classes = "container") {
                    div(classes = "ipad center") {
                        img {
                            src = "https://www.baidu.com/img/bd_logo1.png"
                            width = "270"
                            height = "129"
                        }
                    }

                    form(classes = "form") {
                        input(InputType.text, classes = "form-control ipad") {
                            id = "wd"
                        }
                        button(classes = "btn btn-primary form-control ipad") {
                            id = "baiduBtn"
                            type = ButtonType.submit
                            text("百度一下")

                        }
                    }

                }
            }
        }
    }
}

相比之下,我們使用 DSL 的風格要比原生 HTML 要簡潔優雅。關鍵是,我們的這個 HTML 是用 Kotlin 寫的,這也就意味著,我們的 HTML 代碼不再是簡單的靜態的前端代碼了。我們完全可以直接使用後端的接口返回數據來給 HTML 元素賦值,我們也完全具備了(當然是完全超越了)諸如 JSP、Freemarker 這樣的視圖模板引擎的各種判斷、循環等的語法功能,因為我們直接使用的是一門強大的編程語言 Kotlin 來寫的 HTML 代碼 。

然後,我們就可以直接在控製器層的代碼裏直接調用我們的 Kotlin 視圖代碼了:

@Controller
class HelloDSLController {
    @Autowired
    var helloDSLView: HelloDSLView? = null

    @GetMapping("hello")
    fun helloDSL(model: Model): ModelAndView {
        model.addAttribute("hello", helloDSLView?.html())
        return ModelAndView("hello")
    }
}

為了簡單起見,我們借用一下 Freemarker 來做視圖解析引擎,但是它隻負責原封不動地來傳輸我們的 Kotlin 視圖代碼。hello.ftl 代碼如下:

${hello}

我們的源碼目錄如下

── src
    ├── main
    │   ├── java
    │   ├── kotlin
    │   │   └── com
    │   │       └── easy
    │   │           └── kotlin
    │   │               └── chapter14_kotlin_dsl
    │   │                   ├── Chapter14KotlinDslApplication.kt
    │   │                   ├── controller
    │   │                   │   └── HelloDSLController.kt
    │   │                   └── view
    │   │                       └── HelloDSLView.kt
    │   └── resources
    │       ├── application.properties
    │       ├── banner.txt
    │       ├── static
    │       │   ├── dsl.css
    │       │   ├── dsl.js
    │       │   └── hello.html
    │       └── templates
    │           └── hello.ftl
    └── test
        ├── java
        ├── kotlin
        │   └── com
        │       └── easy
        │           └── kotlin
        │               └── chapter14_kotlin_dsl
        │                   └── Chapter14KotlinDslApplicationTests.kt
        └── resources

然後,啟動運行 SpringBoot 應用,瀏覽器訪問 https://127.0.0.1:8888/hello , 我們可以看到如下輸出界麵:

螢幕快照 2017-07-23 03.53.07.png

這就是 DSL 的精妙之處。我們後麵可以嚐試使用 kotlinx.html 來寫Kotlin 語言的前端代碼了。在做 Web 開發的時候,我們通常是使用 HTML + 模板引擎(Velocity、JSP、Freemarker 等)來集成前後端的代碼,這讓我們有時候感到很尷尬,要學習模板引擎的語法,還得應對 前端HTML代碼中淩亂的模板引擎標簽、變量等片段代碼。

使用 Kotlin DSL 來寫 HTML 代碼的情況將完全不一樣了,我們將重拾前後端集成編碼的樂趣(不再是模板引擎套前端 HTML,各種奇怪的 #、<#>、${} 模板語言標簽),我們直接把 更加優雅簡單的 DSL 風格的HTML 代碼搬到了後端,同時HTML中的元素將直接跟後端的數據無縫交互,而完成這些的隻是 Kotlin(當然,相應領域的 DSL 基本語義模型還是要學習一下)。

提示:本節項目源碼: https://github.com/EasyKotlin/chapter14_kotlin_dsl

實現一個極簡的http DSL

我們現在已經基本知道 Kotlin 中 DSL 的樣子了。但是這些 DSL 都是怎樣實現的呢?本節我們就通過實現一個極簡的http DSL來學習創建 DSL 背後的基本原理。

在這裏我們對 OkHttp 做一下簡單的封裝,實現一個類似 jquery 中的 Ajax 的 http 請求的DSL。

OkHttp 是一個成熟且強大的網絡庫,在Android源碼中已經使用OkHttp替代原先的HttpURLConnection。很多著名的框架例如Picasso、Retrofit也使用OkHttp作為底層框架。

提示: 更多關於OkHttp 的使用可參考: https://square.github.io/okhttp/

創建 Kotlin Gradle 項目

我們首先使用 IDEA 創建 Kotlin Gradle 項目

螢幕快照 2017-07-23 18.43.04.png

然後,在 build.gradle 裏麵配置依賴

    compile 'com.github.ReactiveX:RxKotlin:2.1.0'
    compile group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.8.1'
    compile group: 'com.alibaba', name: 'fastjson', version: '1.2.35'

其中,RxKotlin是ReactiveX 框架對 Kotlin 語言的支持庫。我們這裏主要用RxKotlin來進行請求回調的異步處理。

我們使用的是 'com.github.ReactiveX:RxKotlin:2.1.0' , 這個庫是在 https://jitpack.io 上,所以我們在repositories配置裏添加 jitpack 倉庫

repositories {
    maven { url 'https://jitpack.io' }
    ...
}

RxKotlin

ReactiveX是Reactive Extensions的縮寫,一般簡寫為Rx,最初是LINQ的一個擴展,由微軟的架構師Erik Meijer領導的團隊開發,在2012年11月開源。

Rx擴展了觀察者模式用於支持數據和事件序列。Rx是一個編程模型,目標是提供一致的編程接口,幫助開發者更方便的處理異步I/O(非阻塞)數據流。

Rx庫支持.NET、JavaScript和C++ 。Rx近幾年越來越流行,現在已經支持幾乎全部的流行編程語言了。一個語言列表如下所示:

Rx 支持的編程語言 項目主頁
Java RxJavahttps://github.com/ReactiveX/RxJava
JavaScript  RxJShttps://github.com/ReactiveX/rxjs
C#  Rx.NEThttps://github.com/Reactive-Extensions/Rx.NET
C#(Unity)  UniRxhttps://github.com/neuecc/UniRx
Scala  RxScalahttps://github.com/ReactiveX/RxScala
Clojure  RxClojurehttps://github.com/ReactiveX/RxClojure
C++  RxCpphttps://github.com/Reactive-Extensions/RxCpp
Lua  RxLuahttps://github.com/bjornbytes/RxLua
Ruby  Rx.rbhttps://github.com/Reactive-Extensions/Rx.rb
Python: RxPYhttps://github.com/ReactiveX/RxPY
Go  RxGohttps://github.com/ReactiveX/RxGo
Groovy  RxGroovyhttps://github.com/ReactiveX/RxGroovy
JRuby  RxJRubyhttps://github.com/ReactiveX/RxJRuby
Kotlin  RxKotlinhttps://github.com/ReactiveX/RxKotlin
Swift  RxSwifthttps://github.com/kzaher/RxSwift
PHP  RxPHPhttps://github.com/ReactiveX/RxPHP
Elixir  reaxivehttps://github.com/alfert/reaxive
Dart  RxDarthttps://github.com/ReactiveX/rxdart

Rx的大部分語言庫由ReactiveX這個組織負責維護。Rx 比較流行的庫有RxJava/RxJS/Rx.NET等,當然未來RxKotlin也必將更加流行。

提示: Rx 的社區網站是: https://reactivex.io/ 。 Github 地址:https://github.com/ReactiveX/

Http請求對象封裝類

首先我們設計Http請求對象封裝類如下

class HttpRequestWrapper {

    var url: String? = null

    var method: String? = null

    var body: RequestBody? = null

    var timeout: Long = 10

    internal var success: (String) -> Unit = {}
    internal var fail: (Throwable) -> Unit = {}

    fun success(onSuccess: (String) -> Unit) {
        success = onSuccess
    }

    fun error(onError: (Throwable) -> Unit) {
        fail = onError
    }
}

HttpRequestWrapper的成員變量和函數說明如下表

成員 說明
url 請求 url
method 請求方法,例如 Get、Post 等,不區分大小寫
body 請求頭,為了簡單起見我們直接使用 OkHttp的RequestBody類型
timeout 超時時間ms,我們設置了默認值是10s
success 請求成功的函數變量
fail 請求失敗的函數變量
fun success(onSuccess: (String) -> Unit) 請求成功回調函數
fun error(onError: (Throwable) -> Unit) 請求失敗回調函數

http 執行引擎

我們直接調用 OkHttp 的 Http 請求 API

private fun call(wrap: HttpRequestWrapper): Response {

    var req: Request? = null
    when (wrap.method?.toLowerCase()) {
        "get" -> req = Request.Builder().url(wrap.url).build()
        "post" -> req = Request.Builder().url(wrap.url).post(wrap.body).build()
        "put" -> req = Request.Builder().url(wrap.url).put(wrap.body).build()
        "delete" -> req = Request.Builder().url(wrap.url).delete(wrap.body).build()
    }

    val http = OkHttpClient.Builder().connectTimeout(wrap.timeout, TimeUnit.MILLISECONDS).build()
    val resp = http.newCall(req).execute()
    return resp
}

它返回請求的響應對象Response。

我們在OkHttpClient.Builder().connectTimeout(wrap.timeout, TimeUnit.MILLISECONDS).build()中設置超時時間的單位是 TimeUnit.MILLISECONDS

我們通過wrap.method?.toLowerCase()處理請求方法的大小寫的兼容。

使用 RxKotlin 完成請求響應的異步處理

我們首先新建一個數據發射源:一個可觀察對象(Observable),作為發射數據用

    val sender = Observable.create<Response>({
        e ->
        e.onNext(call(wrap))
    })

其中,e 的類型是 io.reactivex.Emitter (發射器),它的接口定義是

public interface Emitter<T> {
    void onNext(@NonNull T value);
    void onError(@NonNull Throwable error);
    void onComplete();
}

其方法功能簡單說明如下:

方法 功能
onNext 發射一個正常值數據(value)
onError 發射一個Throwable異常
onComplete 發射一個完成的信號

這裏,我們通過調用onNext方法,把 OkHttp 請求之後的響應對象Response 作為正常值發射出去。

然後我們再創建一個數據接收源:一個觀察者(Observer)

    val receiver: Observer<Response> = object : Observer<Response> {
        override fun onNext(resp: Response) {
            wrap.success(resp.body()!!.string())
        }

        override fun onError(e: Throwable) {
            wrap.fail(e)
        }

        override fun onSubscribe(d: Disposable) {
        }

        override fun onComplete() {
        }

    }

receiver 的 onNext 函數接收 sender 發射過來的數據 Response, 然後我們在函數體內,調用這個響應對象,給 wrap.success 回調函數進行相關的賦值操作。同樣的,onError 函數中也執行相應的賦值操作。

最後,通過 subscribe 訂閱函數來綁定 sender 與 receiver 的關聯:

sender.subscribe(receiver)

作為接收數據的 receiver (也就是 觀察者 (Observer) ),對發送數據的 sender (也就是可被觀察對象( Observable)) 所發射的數據或數據序列作出響應。

這種模式可以極大地簡化並發操作,因為它創建了一個處於待命狀態的觀察者,在未來某個時刻響應 sender 的通知,而不需要阻塞等待 sender 發射數據。這個很像協程中的通道編程模型。

DSL主函數 ajax

我們的ajax DSL主函數設計如下:

fun ajax(init: HttpRequestWrapper.() -> Unit) {
    val wrap = HttpRequestWrapper()
    wrap.init()
    doCall(wrap)
}

其中,參數init: HttpRequestWrapper.() -> Unit 是一個帶接收者的函數字麵量,它的類型是init = Function1<com.kotlin.easy.HttpRequestWrapper, kotlin.Unit>。 HttpRequestWrapper是擴展函數init()的接收者,點號 . 是擴展函數修飾符。

我們在函數體內直接調用了這個函數字麵量 wrap.init() 。這樣的寫法可能比較難以理解,這個函數字麵量 init 的調用實際上是 init.invoke(wrap) ,就是把傳入 ajax 的函數參數直接傳遞給 wrap 。為了更簡單的理解這個 init 函數的工作原理,我們通過把上麵的 ajax 函數的代碼反編譯成對應的 Java 代碼如下:

   public static final void ajax(@NotNull Function1 init) {
      Intrinsics.checkParameterIsNotNull(init, "init");
      HttpRequestWrapper wrap = new HttpRequestWrapper();
      init.invoke(wrap);
      doCall(wrap);
   }

也就是說,ajax 函數的一個更容易理解的寫法是

fun ajax(init: HttpRequestWrapper.() -> Unit) {
    val wrap = HttpRequestWrapper()
    init.invoke(wrap)
    doCall(wrap)
}

我們在實際應用的時候,可以直接把 init 寫成Lambda 表達式的形式,因為接收者類型HttpRequestWrapper 可以從上下文推斷出來。

我們這樣調用 ajax 函數:

ajax {
    url = testUrl
    method = "get"
    success {
        string ->
        println(string)
        Assert.assertTrue(string.contains("百度一下"))
    }
    error {
        e ->
        println(e.message)
    }
}

下麵是幾個測試代碼示例:

package com.kotlin.easy

import com.alibaba.fastjson.JSONObject
import okhttp3.MediaType
import okhttp3.RequestBody
import org.junit.Assert
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4

/**
 * Created by jack on 2017/7/23.
 */

@RunWith(JUnit4::class)
class KAjaxTest {

    @Test fun testHttpOnSuccess() {
        val testUrl = "https://www.baidu.com"
        ajax {
            url = testUrl
            method = "get"
            success {
                string ->
                println(string)
                Assert.assertTrue(string.contains("百度一下"))
            }
            error {
                e ->
                println(e.message)
            }
        }

    }

    @Test fun testHttpOnError() {
        val testUrl = "https://www2.baidu.com"

        ajax {
            url = testUrl
            method = "get"
            success {
                string ->
                println(string)
            }
            error {
                e ->
                println(e.message)
                Assert.assertTrue("connect timed out" == e.message)
            }
        }
    }


    @Test fun testHttpPost() {
        var json = JSONObject()
        json.put("name", "Kotlin DSL Http")
        json.put("owner", "Kotlin")
        val postBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), json.toString())
        ajax {
            url = "saveArticle"
            method = "post"
            body = postBody
            success {
                string ->
                println(string)
            }
            error {
                e ->
                println(e.message)
            }
        }
    }


    @Test fun testLambda() {
        val testUrl = "https://www.baidu.com"

        val init: HttpRequestWrapper.() -> Unit = {
            this.url = testUrl
            this.method = "get"
            this.success {
                string ->
                println(string)
                Assert.assertTrue(string.contains("百度一下"))
            }
            this.error {
                e ->
                println(e.message)
            }
        }
        ajax(init)
    }

到這裏,我們已經完成了一個極簡的 Kotlin Ajax DSL。

本節工程源碼: https://github.com/EasyKotlin/chatper14_kotlin_dsl_http

本章小結

相比於Java,Kotlin對函數式編程的支持更加友好。Kotlin 的擴展函數和高階函數(Lambda 表達式),為定義Kotlin DSL提供了核心的特性支持。

使用DSL的代碼風格,可以讓我們的程序更加直觀易懂、簡潔優雅。如果使用Kotlin來開發項目的話,我們完全可以去嚐試一下。

最後更新:2017-09-14 09:33:27

  上一篇:go  支付寶社交版
  下一篇:go  Kotlin 簡單優雅的高階函數