赞
踩
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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。