技巧1:使用别名使业务类型表达更清晰

例如,我们有一个PhotoUploader接口,它拥有一个uploadPhoto方法用以上传图片数据,并返回一个String类型的URL:

interface PhotoUploader {

suspend fun uploadPhoto(bytes: ByteArray): Result

}

现在的问题是,如果这个接口的调用者在不看注释的情况下,如何知道这个方法的返回值代表的是一个URL呢?

这个时候,一个好用的小技巧就是使用 kotlin 别名来定义返回值的类型,例如:

typealias PhotoUrl = String

interface PhotoUploader {

suspend fun uploadPhoto(bytes: ByteArray): Result

}

这里我们使用 typealias 显示的定义一个String类型的别名为PhotoUrl,这样调用者在看到uploadPhoto方法的返回值时,就一眼能够看出它返回的是一个Url,而不是其他的什么东西。

再举个例子,比如下面代码:

// 返回值是一个Map, key代表用户id,value代表作者的名字

val result : Map = getFromSomeBusiness()

显然如果没有注释,没有人会知道你返回的是啥玩意。但是我们使用别名可以很好的解决这个问题:

typealias IdToAuthorName = Map

val result : IdToAuthorName = getFromSomeBusiness()

我们看到 IdToAuthorName 这个类型就一目了然了。

不得不说别名是一个非常好用的东西,它能够使复杂的类型表达简单化、清晰化,而且越复杂的类型越有优势。

技巧2:使用更加人道的变量命名

请看下面的代码:

data class User(val id: String, val name: String, val isOnline: Boolean)

fun messageOnlineUsers(usersInRoom: List, message: String) {

val partitionedUsers = usersInRoom.partition { it.isOnline }

partitionedUsers.first.forEach { user ->

user.sendMessage(message)

}

}

private fun User.sendMessage(message: String) {

//···

}

这个代码中 messageOnlineUsers 函数的意图是过滤出usersInRoom这个列表中的所有在线用户,然后将message群发给所有在线用户。这里使用了一个kotlin的集合类的扩展函数partition,它的作用是根据传入的条件把集合分成两部分封装成一个Pair, List>对象,其中Pair.first表示满足条件为true的结果,而Pair.second表示满足条件为false的结果。

但这段代码可读性不是很好,当我们看到这段代码时,首先我们要去看一下partition函数的定义,然后还要理解一下整段代码的逻辑结构,最后在脑子里做一番思考挣扎之后才能明白过来:哦,原来partitionedUsers.first表示的就是过滤出来的在线用户啊~ 显然,这理解起来废了不少劲,对于头发数量非常金贵的程序员来说无疑是雪上加霜。

那有没有更好的方法呢?

我们可以使用 Kotlin 的解构语法来解决此问题。还记得什么是 Kotlin 的解构语法吗,回忆一下,它最多的出现场景是在使用数据类的时候,例如:

val book = Book(0, "Kotlin in Action", "Dmitry")

val (id, name, author) = book

所以对于上面的代码我们可以这样改一下:

fun messageOnlineUsers(usersInRoom: List, message: String) {

val (onlineUsers, offlineUsers) = usersInRoom.partition { it.isOnline }

onlineUsers.forEach { user ->

user.sendMessage(message)

}

}

这样,是不是无需多言,一眼就懂了呢,我们甚至都不用关心partition这个函数具体是干嘛的。

技巧3:使用属性代理简化读写逻辑

请看下面的代码:

class MainActivity : ComponentActivity() {

private val sharedPreferences by lazy {

getSharedPreferences("my_prefs", MODE_PRIVATE)

}

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

sharedPreferences.edit().putString("token", "Hello world").apply()

val token = sharedPreferences.getString("token", null)

println(token)

setContent {

MyComposeApplicationTheme {

}

}

}

}

这个代码中只是为了从SharedPreferences中读或者写一个字符串的值,但是有没有发现我们要实现这样一个小需求就要写一大坨的东西。

在没有使用 Kotlin 之前,使用Java的时候,或许你有封装过一些工具类来做类似的事情,但是下面我们看一下如何通过 Kotlin 的属性代理的方式来简化类似这种读写数据的逻辑。

做法很简单,我们只需要定义一个包装类,让其实现ReadWriteProperty接口,你需要实现两个接口setValue和getValue,然后将SharedPreferences的读写逻辑移动到对应的setValue和getValue中即可:

class SharedPreferencesDelegate(

private val context: Context,

private val name: String,

private val defaultValue: String = ""

): ReadWriteProperty { // 第一个参数表示拥有该属性的对象,第二个参数表示所代理的属性值的类型

private val sharedPreferences by lazy {

context.getSharedPreferences("my_prefs", ComponentActivity.MODE_PRIVATE)

}

override fun getValue(thisRef: Any?, property: KProperty<*>): String {

return sharedPreferences.getString(name, defaultValue) ?: defaultValue

}

override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) {

sharedPreferences.edit().putString(name, value).apply()

}

}

然后我们可以为Context定义一个扩展函数来使用代理对象:

fun Context.sharedPreferences(name: String) = SharedPreferencesDelegate(this, name)

使用就很简单了:

class MainActivity : ComponentActivity() {

private var token by sharedPreferences("token") // 委托代理给上面定义的代理类

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

token = "hello world" // 会调用上面代理类中的setValue方法

println(token) // 会调用上面代理类中的getValue方法

setContent {

MyComposeApplicationTheme {

}

}

}

}

当然我们只是读取值的话,就可以这样写:

println(token)

这样是不是完全看不出它是从一个SharedPreferences中读取的,这样将读写的逻辑隐藏在了背后,开发者只需要读,但不需要关心具体是怎么读的,能够更好的把精力放在纯业务问题上。

当然这里只是使用SharedPreferences来举例,虽然Google目前已经不推荐使用它了(推荐使用新一代轻量级存储库DataStore,如需了解详情可参考Jetpack架构组件库:DataStore),但这不并妨碍本文以此作为示例来演示问题所在,如果你有其他的比较复杂的读写逻辑想要简化,其实处理方式是类似的。

如果你使用 Compose 开发,对于属性代理一定不会陌生:

class MainActivity : ComponentActivity() {

private var state by mutableStateOf(0)

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

state++

}

}

Compose 中常用的状态定义所使用的 by mutableStateOf() 就是一种属性代理。

注意: 本文部分示例代码为了方便直观,都是直接在 Activity 中演示的,但是实际当中应当遵循良好的架构分层原则,避免出现 MVC 式的大杂烩,例如使用 Jetpack Compose 开发的话,上面的SharedPreferences应该放在 Data Layer 数据层,作为 Data Source 为 Repositories (存储仓库) 提供数据源,而其他UI层(如ViewModel)必须通过Data Layer层暴露的Repositories 来作为访问数据源的唯一途径(详情请参考Jetpack Compose 中的架构思想)。

参考链接

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。