在 kotlin 中从干净的架构定义用例的惯用方法

Idiomatic way to define use cases from clean architecture in kotlin

我尝试将我的用例 classes 从 Java 切换到 Kotlin,从 Rxjava 切换到协程。

这是我的 GetTopSongs class.

class GetTopSongs {

    suspend fun execute(limit:Int):Either<List<Song>>{
        //... do stuff
    }
}

我想知道在Kotlin中有没有比getTopSongs.execute(10)更好的方法?

使用operator overloading

class GetTopSongs {

    suspend operator fun invoke(limit:Int):Either<List<Song>>{
        //... do stuff
    }
}

因此您可以忽略额外的 .execute 调用:

val getTopSongs = GetTopSongs()
val result = getTopSongs(10)

如果您要使用 Kotlin 1.3,那么您可以使用 "callable references to a suspend function"。您向 ViewModel 构造函数提供函数类型:

class MyViewModel(val usecase: suspend (Int) -> List<Int>) {
    suspend fun doIt() {
        println(usecase(5))
    }
}

您的用例可以归为一个或多个 类:

class MyUsecasses(val myRepo: MyRepo) {
    suspend fun usecase1(para: Int): List<Int> = myRepo...
    suspend fun usecase2(para: String): List<String> = myRepo...
}

创建 ViewModel 时提供函数参考:

val usecases = MyUsecasses(repo)
val viewmodel = MyViewModel(usecases::usecase1)

如果您不喜欢 ViewModel 构造函数中的函数类型,您可以使用类型别名:

typealias Usecase1 = suspend (Int) -> List<Int>
class MyViewModel(val usecase: Usecase1) 

首先要注意的是 Either 是你在像 RxJava 这样的纯 FP API 中需要的拐杖。在某种程度上,将 mapskipUntil 等运算符链接起来会产生漂亮的代码,但您必须学习大量的运算符词汇表,并且在某些情况下您仍然需要其他东西。

Kotlin 协程的最大好处是您可以继续使用普通的旧命令式编程风格,并具有控制流语句的全部功能,包括 try-catch。因此我推荐以下签名:

class GetTopSongs {
    suspend operator fun invoke(limit:Int): List<Song> {
       val result = ...
       if (someProblem) throw MyException("problem description")
       else return result
    }
}

回到命令式风格的一个不太明显的好处是你不会 运行 陷入那些长达一页的类型推断错误。