这可能是我目前看的最舒服的Kotlin数据Handle类了

前言

毕竟是第一次在这上面写东西,感觉开头不写点什么东西不太好,不太严谨。那我就来个严谨的吧。在我们开发当中不可避免的会与数据打交道,数据可以来自network,local...因此很快就会想到一个模版:

kotlin 复制代码
class XxxRep {
    suspend fun getData(): Result<YourData>
 }
 
class XxxUseCase(
    private val xxRepImpl: XxxRep
) {
    operator fun invoke() {
        xxxRepImpl.getData()
        ///
    }
}

有木有那种感觉了.接下来我们就要针对结果的success和failure去进行处理就行了,现在我要讲的就是包装这个Result。

a. sealed interface

这里先看一下代码

kotlin 复制代码
    sealed class Result<T>(val data : T?= null, val message: String? = null) {
        data class Success<T>(val data: T?): Result<T>(data)
        data class Error<T>(val message: String? = null, val data: T? = null): Result<T>(data, message)
    }

是不是通用的密封类看起来觉得还行,但是我现在不用它了。理由无他,我不想写那么多的参数。开玩笑的,一方面有这理由,还有一方面就是我认为具体的message不应该是string直接在data层获取并一步步传递到domain层再到presentation层,我们应该包装成一种状态的形式用于表示error。大白话就是用个枚聚类表示error状态,然后定义些许个string。

csharp 复制代码
sealed interface Error  
  
typealias RootError = Error  
  
sealed interface Result<out D, out E : RootError> {  
    data class Success<out D, out E : RootError>(val data: D) : Result<D, E>  
    data class Error<out D, out E : RootError>(val error: E) : Result<D, E>  
}  
  

我来解释一下👀 首先我定义了一个顶层Error,这意味着我们后续所有模块中的Error都是它的弟弟,哦不,儿子。

kotlin 复制代码
sealed interface DataError: Error {  
    enum class NetWorkError : DataError {  
        NO_INTERNET,  
        TOO_MANY_REQUESTS,  
        UNEXPECTED_TOKEN,  
        PAYLOAD,  
        REQUEST_TIMEOUT  
    }  

    enum class LocalError: DataError {  
        SERIALIZATION,  
        UNKNOWN  
        }  
}

sealed interface XxxUseCaseError : Error {  
    enum class FinalError : XxxUseCaseError {  
        ABC_ERROR,  
        CHUNK_ERROR  
    }  
}

然后我用typealias取了个别名,这个没有啥特别含义啊,就是单纯命名以及泛型名称一样会报错罢了。 最后和之前的一样,定义了data,error并用out修饰,意思就是提前口头警告你小子这些个参数改不了,只能给爷乖乖读。

b. Data

核心已经结束了,剩下的就是怎么串联起来了,我们只要将上面的rep的改一下。

kotlin 复制代码
interface AuthRep {  
    suspend fun registerUser(name: String?): Result<User, DataError>  
}  
  
class NetAuthRepImpl: AuthRep {  
    override suspend fun registerUser(name: String?): Result<User, DataError> {  
        return Result.Error(DataError.NetWorkError.NO_INTERNET)  
    }  
}  
  
class LocalAuthRepImpl: AuthRep {  
    override suspend fun registerUser(name: String?): Result<User, DataError> {  
        return Result.Error(DataError.LocalError.SERIALIZATION)  
    }  
}  
  
data class User(  
    val id: Int,  
    val name: String,  
    val email: String  
)

剩下的大家懂得都懂了,我们只要在viewModel或者useCase中去使用when分别处理结果就行了。

scss 复制代码
class RegisterUserUseCase(  
    private val netAuthRepImpl: AuthRep = NetAuthRepImpl(),  
    private val localAuthRepImpl: AuthRep = LocalAuthRepImpl()  
) {  
  
suspend fun registerUser(name: String) {  
    when(val result = netAuthRepImpl.registerUser(name)) {  
    is Result.Error -> { handleNetWorkError(result.error) }  
    is Result.Success -> TODO()  
    } 
}  
  
private fun handleNetWorkError(error: DataError) {  
    when(error) {  
        DataError.LocalError.SERIALIZATION -> TODO()  
        DataError.LocalError.UNKNOWN -> TODO()  
        DataError.NetWorkError.NO_INTERNET -> TODO()  
        DataError.NetWorkError.TOO_MANY_REQUESTS -> TODO()  
        DataError.NetWorkError.UNEXPECTED_TOKEN -> TODO()  
        DataError.NetWorkError.PAYLOAD -> TODO()  
        DataError.NetWorkError.REQUEST_TIMEOUT -> TODO()  
    }  
}  
  

到这里好像一下子就明朗起来了✌️,结束!

塔塔开

实在想不出来取什么标题了,最近刚又补完进击的巨人,就鬼使神差了。到这里我们再做一个扩展,其实对于我们来说,前期的努力是为了后期的装杯,因此到这里还没完。可以看到我的useCase中只用了一个netAuthImpl的方法,其实在我们实际场景中我们基本避免不了网络获取数据,本地存储数据这一个单向流操作。因此我们又很容易写出这样的:

kotlin 复制代码
when(val result = netAuthRepImpl.registerUser(name)) {  
        is Result.Error -> { handleNetWorkError(result.error) }  
        is Result.Success -> {  
        localAuthRepImpl.registerUser(result.data.name)  
    }  
}
    

MD确实好像也没啥问题,也不错,淦,那要不结束了吧。怎么可能,我这个例子比较简单罢了。我想要的是一种简洁明了跟读手册一样啊くそやろ!

kotlin 复制代码
inline fun <D, E : RootError, R> Result<D, E>.andThen(block: D.() -> Result<R, E>): Result<R, E> {  
    return when (this) {  
        is Result.Error -> {  
            Result.Error(this.error)  
        }  

        is Result.Success -> {  
            block(this.data)  
        }  
    }  
}  
  
  
inline fun <D, E : RootError> Result<D, E>.onSuccess(action: (value: D) -> Unit): Result<D, E> {  
    if (this is Result.Success) {  
        action(this.data)  
    }  
    return this  
}  
  
inline fun <D, E : RootError> Result<D, E>.onFailure(action: (error: E) -> Unit): Result<D, E> {  
    if (this is Result.Error) {  
        action(this.error)  
    }  
    return this  
}

fun Result<*, DataError>.asUnitText() {  
if (this is Result.Error) {  
    when (this.error) {  
        DataError.LocalError.SERIALIZATION -> "Ooops, There is some error when serializing the data!"  
        DataError.LocalError.UNKNOWN -> TODO()  
        DataError.NetWorkError.NO_INTERNET -> TODO()  
        DataError.NetWorkError.TOO_MANY_REQUESTS -> TODO()  
        DataError.NetWorkError.UNEXPECTED_TOKEN -> TODO()  
        DataError.NetWorkError.PAYLOAD -> TODO()  
        DataError.NetWorkError.REQUEST_TIMEOUT -> TODO()  
    }  
    } else {  
        throw Exception()  
    }

我扩展了Result方法,很简单,简单的让我误以为自己很厉害了。

kotlin 复制代码
return netAuthRepImpl.registerUser(name).andThen {  
    localAuthRepImpl.registerUser(this.name)  
}.onSuccess {  
    // do works  
}.onFailure {  
    handleNetWorkError(it)  
}

简洁明了吧,over~😏

最后

其中可能涉及了一些架构术语,像domain,usecase诸如此类,其实这是来自整洁架构的定义。有兴趣的朋友可以了解一下,我简单介绍一下,核心就是将整个结构分为三部分,数据,操作,显示三个域,每个域互相隔离。真特么简单,这个介绍😄(我自己先吐槽一下)。

作为一个老看客了,距离上一次写技术博客还是疫情年上大学的时候,我也不知道为什么突然写了,想做就做了,可能人就是这样的生物吧。

少年时你听到人们说应该欣赏日落,但你忙于探索世界,不能静静地坐下来观赏。再后来,你抗拒本应该感觉得到的情感,斥之为庸俗。随着年龄的增长,你不再关心日落在别人眼中的是否庸俗。你用自己的眼睛去看它,内心充满感激和欣喜。

相关推荐
居居飒11 小时前
Android学习(四)-Kotlin编程语言-for循环
android·学习·kotlin
刘争Stanley1 天前
如何高效调试复杂布局?Layout Inspector 的 Toggle Deep Inspect 完全解析
android·kotlin·android 15·黑屏闪屏白屏
sickworm陈浩1 天前
Java 转 Kotlin 系列:究竟该不该用 lateinit?
android·kotlin
droidHZ3 天前
Compose Multiplatform 之旅—声明式UI
android·kotlin
zhangphil3 天前
Android基于Path的addRoundRect,Canvas剪切clipPath简洁的圆角矩形实现,Kotlin(1)
android·kotlin
alexhilton5 天前
Android技巧:学习使用GridLayout
android·kotlin·android jetpack
zhangphil6 天前
Android使用PorterDuffXfermode的模式PorterDuff.Mode.SRC_OUT实现橡皮擦,Kotlin(1)
android·kotlin
IH_LZH7 天前
OkHttp源码分析:分发器任务调配,拦截器责任链设计,连接池socket复用
android·java·okhttp·kotlin
casual_clover8 天前
Android之RecyclerView显示数据列表和网格
android·kotlin
氤氲息9 天前
导入kotlin
android·开发语言·kotlin