Baf*_*tek 4 generics android kotlin retrofit
所以,我正在重写我的应用程序代码,使其“干净”(层分离,遵循 Android 团队推荐的 MVVM 模式)
在这里,我有一个简单的 Retrofit 接口来与我的 API 通信
interface Api {
@GET("comments")
suspend fun getPlaceComments(@Query("placeId") placeId: String): Response<List<CommentResponse>>
@POST("comments")
suspend fun addPlaceComment(@Header("placeId") placeId: String, @Header("text") text: String): Response<Unit>
@DELETE("comments")
suspend fun deletePlaceComment(@Header("placeId") placeId: String): Response<Unit>
}
Run Code Online (Sandbox Code Playgroud)
只是一个简单的 CRUD。
现在,上一层,我有我的 SocialRepository。为了避免代码重复,我创建了一个通用方法callSafely,该方法将一个挂起的 API 函数和一个 placeId 作为其参数。
class SocialRepository {
private val client: Api = ApiClient.webservice
private suspend fun <T> callSafely(
apiMethod: suspend (placeId: String) -> Response<T>,
placeId: String,
): T? {
Log.d(TAG, "$apiMethod called safely")
var response: Response<T>? = null
try {
response = apiMethod(placeId)
} catch (e: Exception) {
e.printStackTrace()
}
if (response?.isSuccessful != true) {
Log.w(TAG, "response.isSuccessful isn't true.")
}
return response?.body()
}
suspend fun getPlaceComments(placeId: String): List<CommentResponse>? {
return callSafely(client::getPlaceComments, placeId)
}
suspend fun deletePlaceComment(placeId: String): Unit? {
return callSafely(client::deletePlaceComment, placeId)
}
suspend fun addPlaceComment(placeId: String, text: String): Unit? {
return callSafely(client::addPlaceComment, placeId, text) // HERE LIES THE PROBLEM
// I can't pass additional data because the method signature won't match with what's defined in callSafely()
}
}
Run Code Online (Sandbox Code Playgroud)
现在,它工作得很好,当然我还有我的 Activity 和它的 ViewModel,ViewModel 调用存储库中的一个方法等等。没关系。
重要的是添加地点评论需要额外的数据,例如评论的实际文本。获取和删除评论只需要 placeId,而在添加评论时,其内容text也是必需的。我读过vararg在 Kotlin中传递函数是不可能的。我也不想用 a 之类的东西把所有的 API 方法弄得一团糟,这样的东西List of params大部分时间都是空的,只会造成混乱。
我可以走简单的方法,只需复制callSafelyto的代码addPlaceComment并更改它,但这不是我要找的。我知道如何解决问题,但我不知道如何去做the clean way。将来我可能会添加更多需要额外数据的端点(除了placeId),问题会再次出现。
在这个情况下,你会怎么做?如何写“正确的方法”?
我什至不确定如何正确表达我正在寻找的东西,这就是为什么这篇文章如此漫无边际。很抱歉提前。我真的希望你能帮助我。
“清洁方式”是一个非常广泛的概念。一切都取决于您的需求,并且没有“一种好的做事方式”。
在您的特定情况下,您有多种选择:
1) 类型别名
typealias ApiCall1<P, R> = suspend (P) -> Response<R>
typealias ApiCall2<P1, P2, R> = suspend (P1, P2) -> Response<R>
fun <P> callSafely(param: P, call: ApiCall1<P, YourResult>): YourResult
fun <P1, P2> callSafely(param1: P1, param2: P2, call: ApiCall2<P1, P2, YourResult>): YourResult
Run Code Online (Sandbox Code Playgroud)
2) 可变参数
fun callSafely(vararg params: String, call: suspend (arr: Array<String>) -> YourResult {
...
call(*params)
...
}
Run Code Online (Sandbox Code Playgroud)
3)Lambdas (最适合您的情况)
没有人强迫您使用方法引用。在需要时使用 lambda。但是将 lambda 作为“更干净”代码的最后一个参数。
private suspend fun <T> callSafely(
placeId: String,
apiMethod: suspend (placeId: String) -> Response<T>
): T?
suspend fun getPlaceComments(placeId: String): List<CommentResponse>? {
return callSafely(placeId, client::getPlaceComments)
}
suspend fun deletePlaceComment(placeId: String): Unit? {
return callSafely(placeId, client::deletePlaceComment)
}
suspend fun addPlaceComment(placeId: String, text: String): Unit? {
return callSafely(placeId) { id -> client.addPlaceComment(id, text) }
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
386 次 |
| 最近记录: |