美文网首页
Kotlin 领域专用语言DSL

Kotlin 领域专用语言DSL

作者: 大鹏的鹏 | 来源:发表于2019-08-15 14:55 被阅读0次

一、领域特定语言 DSL的概念

DSL(domain specific language),即领域专用语言:专门解决某一特定问题的计算机语言。由于它是以简洁的形式进行表达,整体上直观易懂,使得调用代码和读代码的成本都得以降低,即使是不懂编程语言的一般人都可以进行使用。比如大家耳熟能详的 SQL 和正则表达式、软件构建领域 Ant、UI 设计师 HTML。所谓领域也就是限定语言是适用于一定范围的。CSS 专注定义我们页面结构和样式,包括颜色和尺寸等等,而 SQL 专注于对数据的操作。

DSL 分为外部 DSL内部 DSL

外部DSL:在主程序设计语言之外,用一种单独的语言表示领域专有语言。可以是定制语法,或者遵循另外一种语法,如 XML、JSON。(从零开始构建的语言,需要实现语法解析器等)

内部 DSL:通常是基于通用编程语言实现,具有特定的风格,如 iOS 的依赖管理组件 CocoaPods 和 Android 的主流编译工具 Gradle。(从一种宿主语言构建而来)

通用编程语言和DSL的区别:

通用编程语言(如 Java、Kotlin、Android等),往往提供了全面的库来帮助开发者开发完整的应用程序,而 DSL 只专注于某个领域,比如 SQL 仅支持数据库的相关处理,而正则表达式只用来检索和替换文本,我们无法用 SQL 或者正则表达式来开发一个完整的应用。

DSL 与通用编程语言的区别:

DSL 供非程序员使用,供领域专家使用;
DSL 有更高级的抽象,不涉及类似数据结构的细节;
DSL 表现力有限,其只能描述该领域的模型,而通用编程语言能够描述任意的模型;

二、Kotlin的DSL特性支持

许多现代语言为创建内部 DSL 提供了一些先进的方法, Kotlin 也不例外。

在Kotlin 中创建 DSL , 一般主要使用下面两个特性:

  • 扩展函数、扩展属性。
  • 带接收者的 Lambda 表达式(高阶函数)。
1.例子1

在日常开发中,我们使用DSL语言主要在gradle文件中:

dependencies {
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    implementation 'android.arch.lifecycle:extensions:1.1.1'
    implementation 'androidx.work:work-runtime:2.3.4'
}

gradle就知道我们到底依赖了哪些库。使用kotlin来进行实现:

  1. 首先创建DSL.kt文件,我们在里面定义Dependency类:

    class Dependency {
      // 定义个 list 来保存所有依赖库
      val libraries = ArrayList<String>()
    
      // 定义向 list 中添加依赖库的方法
      fun implementation(lib: String) {
          libraries.add(lib)
      }
    }
    
  2. 如果是常规用法,我们会这样写:

    val dependency = Dependency()
    dependency.implementation("lib1")
    dependency.implementation("lib2")
    
  3. 如果你再熟悉一点Kotlin,可以优化一下:

    val dependency = Dependency().apply{
        implementation("lib1")
        implementation("lib2")
    }
    

    目前的代码结构已经很接近DSL语法了,如果我们能把Dependency()的创建隐藏起来就更完美了。

  4. 接下来定义一个 dependencies 高阶函数如下:

      // 把函数类型参数定义到 Dependency 类中,调用它时先创建实例,通过实例调用函数类型参数
      // 传入的 Lambda 表达式就能得到执行,最后把保存的依赖库集合返回
      fun dependencies(block: Dependency.() -> Unit): List<String> {
          val dependency = Dependency()
          dependency.block()
          return dependency.libraries
      }
    
  5. 用法

    val dependency = dependency{
        implementation("lib1")
        implementation("lib2")
      }
    
2.例子2

对OkHttp做一下简单的封装,只是为了演示如何实现dsl。

class RequestWrapper {
    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 onSuccess(onSuccess: (String) -> Unit) {
        _success = onSuccess
    }

    fun onFail(onError: (Throwable) -> Unit) {
        _fail = onError
    }
}

fun http(init: RequestWrapper.() -> Unit) {
    val wrap = RequestWrapper()
    wrap.init()
    executeForResult(wrap)
}

private fun executeForResult(wrap: RequestWrapper) {
    Flowable.create<Response>({ e ->
        onExecute(wrap)?.let { e.onNext(it) }
    }, BackpressureStrategy.BUFFER)
        .subscribeOn(Schedulers.io())
        .subscribe(
            { resp ->
                wrap._success(resp.body()!!.string())
            },
            { e -> wrap._fail(e) })
}

private fun onExecute(wrap: RequestWrapper): Response? {
    var req: Request? = null
    when (wrap.method) {
        "get", "Get", "GET" -> req = Request.Builder().url(wrap.url).build()
        "post", "Post", "POST" -> req = Request.Builder().url(wrap.url).post(wrap.body).build()
        "put", "Put", "PUT" -> req = Request.Builder().url(wrap.url).put(wrap.body).build()
        "delete", "Delete", "DELETE" -> req =
            Request.Builder().url(wrap.url).delete(wrap.body).build()
    }
    val http = OkHttpClient.Builder().connectTimeout(wrap.timeout, TimeUnit.SECONDS).build()
    val resp = http.newCall(req).execute()
    return resp
}

封装完OkHttp之后,看看如何来编写get请求:

http {
    url = "http://www.baidu.com/"
    method = "get"
    onSuccess {
        string -> L.i(string)
    }
    onFail {
        e -> L.i(e.message)
    }
}

总结:DSL语法可有帮助我们节省很多的代码,让代码看上去更规范。

相关文章

网友评论

      本文标题:Kotlin 领域专用语言DSL

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