DSL(domain specific language),即领域专用语言:专门解决某一特定问题的计算机语言,比如大家耳熟能详的 SQL 和正则表达式。
Kotlin DSL的简单定义:“使用 Kotlin 语言开发的,解决特定领域问题,具备独特代码结构的API 。”
假设我们想获取昨天的日期,昨天相对于今天来说也就是一天前,那么获取这个方法的DSL写法就应该是:
val yesterday = 1.days.ago
由上面的结构,我们可以设计扩展函数如下:
fun Int.days() = Period.ofDays(this)
fun Period.ago() = LocalDate.now() - this
//调用
val yesterday = 1.days().ago()
我们只需要修改扩展函数为扩展属性,即可实现最终效果,如下:
val Int.days: Period
get() = Period.ofDays(this)
val Period.ago: LocalDate
get() = LocalDate.now() - this
//调用
val yesterday = 1.days.ago
因此Kotlin DSL有一个显著的特征:近似于口语(个人理解)。
我们先定义一个包含多个方法接口,和需要使用该接口的类。一步步来展示在Kotlin如何使用DSL。
interface Listener {
fun onStart()
fun onNext()
fun onComplete(result:String)
}
class Work {
private var listener: Listener? = null
fun start() {
listener?.run {
onStart()
onNext()
onComplete("onComplete")
}
}
fun addListener(listener: Listener) {
//接口赋值
this.listener = listener
}
//doSomething
}doSomething
}
如下代码,便是这个接口最基本的使用。
val work = Work()
work.addListener(object :Listener{
override fun onStart() {
}
override fun onNext() {
}
override fun onComplete(result:String) {
}
})
思考:日常开发中接口中的每一个方法都不一定必须会使用。如上频繁使用该接口,但是我们很多时候只关心onComplete方法中的回调值,所以每次都书写onStart,onNext会造成很多的代码冗余。怎么去解决这个问题呢?
在Java开发中,我们通常会使用继承写一个中间类,来避免接口方法过多的情况。如下:
open class AListener : Listener {
override fun onStart() {
}
override fun onNext() {
}
override fun onComplete(result: String) {
}
}
这时我们需要哪个方法,重写哪个方法即可:
work.addListener(object : AListener() {
override fun onComplete(result: String) {
super.onComplete(result)
}
})
学习了Kotlin之后,我们发现函数可以作为另一个函数的参数(此时这个函数叫高阶函数),我们可以把上面接口方法的作为函数参数传给扩展函数,简化调用:
//别名
typealias onEmpty = () -> Unit
typealias onResult = (String) -> Unit
inline fun Work.addListenerHigh(
crossinline onStart: onEmpty = {},
crossinline onNext: onEmpty = {},
crossinline onComplete: onResult = { result -> }) {
this.addListener(object : Listener {
override fun onStart() {
onStart.invoke()
}
override fun onNext() {
onNext.invoke()
}
override fun onComplete(result: String) {
onComplete.invoke(result)
}
})
}
这时候我们只需要调用扩展函数,传入自己关注的接口方法即可。
work.addListenerHigh(onComplete = { result: String -> {} })
改造后的高阶函数,已经非常接近DSL结构,其实这里只是利用了Kotlin的具名参数的特性,并不是真正意义上的DSL结构。
接下来我们再进一步利用高阶含函数的特性将其改造成DSL结构。
我们先定义一个类,这个类的属性值都是上面接口对应的函数参数。
//别名
typealias onEmpty = () -> Unit
typealias onResult = (String) -> Unit
class ListenerDSL {
var _onStart: onEmpty? = null
var _onNext: onEmpty? = null
var _onComplete: onResult? = null
fun onStart(onEmpty: onEmpty) {
_onStart = onEmpty
}
fun onNext(onEmpty: onEmpty) {
_onNext = onEmpty
}
fun onComplete(onNext: onResult) {
_onComplete = onNext
}
}
然后使用扩展函数的编写DSL:
fun Work.addListenerDSL(init: ListenerDSL.() -> Unit) {
val listenerDSL = ListenerDSL()
listenerDSL.init()
this.addListener(object : Listener {
override fun onStart() {
listenerDSL._onStart?.invoke()
}
override fun onNext() {
listenerDSL._onNext?.invoke()
}
override fun onComplete(result: String) {
listenerDSL._onComplete?.invoke(result)
}
})
}
这个时候我们只需要调用:
val work = Work()
work.addListenerDSL {
onStart { println("onStart") }
onComplete { result -> println(result) }
}
work.start()
这个时候的写法就很赏心悦目了。
https://www.jianshu.com/p/f5f0d38e3e44
https://www.jianshu.com/p/5b8c4cc38814