传统的URL建造器是怎么使用的呢?类似于下面这样子:
//以下代码并不能运行,只是为了举例
val url=URLBuilder().protocal(“”).hostname(“”).post(“”).path(“”).addParam(“”,”“).build()
首先让我么们看看完成后的URL建造器的实际运用。
val u=url {
protocal { "http" }
hostname { "www.feintkotlin.com" }
port { 8080 }
path { "article/all" }
paramList {
param {
key { "name" }
value { "feint" }
}
param {
key { "age" }
value { "11" }
}
}
}
整个建造器给人的感觉就像是JSON字符串一样,具有层次结构,看起来清晰、舒服。虽然相较于传统的写法可能输入的代码量会稍微多一些,但也相差不大;为了可读性的提升,这一点小小的牺牲还是值得的。
接下来,让我们看看这样子的写法究竟是怎么实现的。
首先说明一下,将会用到语言特性:
lambda表达式(稍微接触过Kotlin的人,看到那些花括号后应该就能看出来了)
函数参数(在Kotlin中可以将函数作为参数使用)
扩展函数
整个url建造器的最外层是url{…},让我们看看它所对应的代码:
fun url(init: URL.() -> Unit): URL {
val url = URL()
url.init()
return url
}
在这个url的函数中,有一个叫 init 的参数,它的类型是一个函数,并且是作为URL这个类的一个扩展。这样子些有什么好处呢?通过这样一种技巧,我们就可以在url函数的lambda表达式中直接调用URL类中的成员了,也就是之前代码中的 protocal、hostname等等。
URL类的完整代码是这样子的:
class URL(var protocal: String = "http",
var hostname: String = "localhost",
var port: Int = 80,
var path: String = "",
var paramList: MutableMap<String, String> = HashMap()) {
fun protocal(pro: () -> String) {
protocal = pro()
}
fun hostname(host: () -> String) {
hostname = host()
}
fun port(port: () -> Int) {
this.port = port()
}
fun path(path: () -> String) {
this.path = path()
}
fun paramList(init: ParamList.() -> Unit) {
val pL = ParamList()
pL.init()
this.paramList = pL.paramList
}
override fun toString(): String {
var baseUrl = "$protocal://$hostname:$port/$path"
var firstParam = true;
paramList.forEach { key, value ->
baseUrl += if (firstParam) {
firstParam = false
"/?$key=$value"
} else "&$key=$value"
}
return baseUrl
}
}
我们可以看到,为了能够使用lambda表达式,protrocal()、hostname()、port()等等函数都接收了一个函数类型的参数。
由于参数列表paramList还有下一层结构,于是我们就仿造url函数的写法:
fun paramList(init: ParamList.() -> Unit) {
val pL = ParamList()
pL.init()
this.paramList = pL.paramList
}
将paramList的函数参数作为ParamList类的一个扩展。
ParamList类的完整代码是这样的:
class ParamList(var paramList: MutableMap<String, String> = HashMap()) {
fun param(init: Param.() -> Unit) {
val param = Param()
param.init()
paramList[param.key] = param.value
}
}
ParamList中的param又有着更深一层的结构,再一仿造之前写法即可。
其中的Param类的代码是这样的:
class Param(var key: String = "", var value: String = "") {
fun key(key: () -> String) {
this.key = key()
}
fun value(value: () -> String) {
this.value = "=${value()}"
}
}
最后,再让我们看看一个完整的使用例子:
fun main(args: Array<String>) {
var u=url {
protocal { "http" }
hostname { "www.feintkotlin.com" }
port { 8080 }
path { "article/all" }
paramList {
param {
key { "name" }
value { "feint" }
}
param {
key { "age" }
value { "11" }
}
}
}
println("""
|protocal:${u.protocal};
|hostname:${u.hostname};
|port:${u.port};
|path:${u.path};
|full_path:${u.toString()};
|param("name"):${u.paramList["name"]}
""".trimMargin())
}
输出: