继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

如何使用Kotlin的语言特性创建一个层次分明的URL建造器

jeck猫
关注TA
已关注
手记 298
粉丝 74
获赞 402

传统的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字符串一样,具有层次结构,看起来清晰、舒服。虽然相较于传统的写法可能输入的代码量会稍微多一些,但也相差不大;为了可读性的提升,这一点小小的牺牲还是值得的。

接下来,让我们看看这样子的写法究竟是怎么实现的。

首先说明一下,将会用到语言特性:

  1. lambda表达式(稍微接触过Kotlin的人,看到那些花括号后应该就能看出来了)

  2. 函数参数(在Kotlin中可以将函数作为参数使用)

  3. 扩展函数

整个url建造器的最外层是url{...},让我们看看它所对应的代码:

fun url(init: URL.() -> Unit): URL {    val url = URL()
    url.init()    return url
}

在这个url的函数中,有一个叫 init 的参数,它的类型是一个函数,并且是作为URL这个类的一个扩展。这样子些有什么好处呢?通过这样一种技巧,我们就可以在url函数的lambda表达式中直接调用URL类中的成员了,也就是之前代码中的 protocalhostname等等。

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())
}

输出:

5b9132f60001cbc105480164.jpg

输出结果

收藏

原文链接:http://www.apkbus.com/blog-822717-72703.html

打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP