英文:
Kotlin better readability of MutableLiveData Observer
问题
I would like to make my code more readable by simplifying the observers of the MutableLiveData objects. But I'm not really sure how to do that. I thought about moving the contents of each observe method into a separate class, but that would only improve the readability of the fragment and the new class would be confusing again.
我想通过简化MutableLiveData对象的观察者来使我的代码更易读。但我不太确定如何做到这一点。我考虑将每个观察方法的内容移动到单独的类中,但那只会提高片段的可读性,新类可能会再次变得混乱。
private fun HomeViewModel.setupObservers() {
messages.observe(viewLifecycleOwner) { response ->
when (response) {
is Resource.Success -> {
response.data?.let { messagesResponse ->
getMessagesWithImageUrl(chatID, messagesResponse)
}
}
is Resource.Error -> {
response.message?.let { message ->
Log.e(TAG, "An error occurred: $message")
}
hideProgressBarLoadMessages()
hideProgressBarSendMessage()
}
is Resource.Loading -> {}
}
}
messagesWithImageUrl.observe(viewLifecycleOwner) {response ->
when(response) {
is Resource.Error -> {
response.message?.let { message ->
Log.e(TAG, "An error occurred: $message")
}
hideProgressBarLoadMessages()
hideProgressBarSendMessage()
}
is Resource.Loading -> {}
is Resource.Success -> {
response.data?.let { messagesResponse ->
adapterMessage
.differ
.submitList(messagesResponse.thread.values.toList())
binding.recyclerViewMessages
.scrollToPosition(adapterMessage.itemCount-1)
hideProgressBarLoadMessages()
hideProgressBarSendMessage()
}
}
}
}
messageMediaUpload.observe(viewLifecycleOwner) { response ->
when(response) {
is Resource.Error -> {
response.message?.let { message ->
Log.e(TAG, "An error occurred: $message")
}
}
is Resource.Loading -> {
showProgressBarSendMessage()
}
is Resource.Success -> {
response.data?.let {
it.metadata?.let { metadata ->
metadata.name?.let { imageName ->
val content = imageName.dropLast(4)
viewModel.sendMessage(
POSTMessage(content, media = true),
POSTLastMessage(content, media = true, msgRead = true),
chatID,
receiverID)
}
}
}
}
}
}
username.observe(viewLifecycleOwner) { response ->
when(response) {
is Resource.Error -> {
response.message?.let { message ->
Log.e(TAG, "An error occurred: $message")
}
}
is Resource.Loading -> { /* TODO: some loading animation */ }
is Resource.Success -> {
response.data?.let { username ->
binding.headlineText.text = username
}
}
}
}
sendMessage.observe(viewLifecycleOwner) { response ->
when(response) {
is Resource.Error -> {
response.message?.let { message ->
Log.e(TAG, "An error occurred: $message")
}
hideProgressBarSendMessage()
}
is Resource.Loading -> {
showProgressBarSendMessage()
}
is Resource.Success -> {}
}
}
setMessageRead.observe(viewLifecycleOwner) { response ->
when(response) {
is Resource.Error -> {
response.message?.let { message ->
Log.e(TAG, "An error occurred: $message")
}
}
is Resource.Loading -> {}
is Resource.Success -> {}
}
}
}
My Resource class looks like this:
我的Resource类看起来像这样:
sealed class Resource
val data: T? = null,
val message: String? = null) {
class Success
class Error
class Loading
}
If I changed it like this:
如果我像这样更改它:
sealed interface Resource
class Success
class Error
class Loading
}
Then I have to modify methods in my ViewModel as follows:
然后,我必须按以下方式修改我的ViewModel中的方法:
//old Resource
fun getChats() = viewModelScope.launch {
chats.postValue(Resource.Loading())
homeRepository.getChats().collect() {
it.data?.let { getChats ->
setChatIDs(getChats)
getUnreadMessages(getChats)
}
chats.postValue(it)
}
}
//new Resource
private fun getChats() = viewModelScope.launch {
chats.postValue(Resource.Loading())
homeRepository.getChats().collect() {
if(it is Resource.Success) {
val data = (it as Resource.Success).data
setChatIDs(data)
getUnreadMessages(data)
}
chats.postValue(it)
}
}
That would make my Observer methods more readable, but I'm not sure if that would be bad practice in the ViewModel.
这将使我的观察者方法更易读,但我不确定在ViewModel中是否会是不好的实践。
英文:
I would like to make my code more readable by simplifying the observers of the MutableLiveData objects. But I'm not really sure how to do that. I thought about moving the contents of each observe method into a separate class, but that would only improve the readability of the fragment and the new class would be confusing again.
private fun HomeViewModel.setupObservers() {
messages.observe(viewLifecycleOwner) { response ->
when (response) {
is Resource.Success -> {
response.data?.let { messagesResponse ->
getMessagesWithImageUrl(chatID, messagesResponse)
}
}
is Resource.Error -> {
response.message?.let { message ->
Log.e(TAG, "An error occurred: $message")
}
hideProgressBarLoadMessages()
hideProgressBarSendMessage()
}
is Resource.Loading -> {}
}
}
messagesWithImageUrl.observe(viewLifecycleOwner) {response ->
when(response) {
is Resource.Error -> {
response.message?.let { message ->
Log.e(TAG, "An error occurred: $message")
}
hideProgressBarLoadMessages()
hideProgressBarSendMessage()
}
is Resource.Loading -> {}
is Resource.Success -> {
response.data?.let { messagesResponse ->
adapterMessage
.differ
.submitList(messagesResponse.thread.values.toList())
binding.recyclerViewMessages
.scrollToPosition(adapterMessage.itemCount-1)
hideProgressBarLoadMessages()
hideProgressBarSendMessage()
}
}
}
}
messageMediaUpload.observe(viewLifecycleOwner) { response ->
when(response) {
is Resource.Error -> {
response.message?.let { message ->
Log.e(TAG, "An error occurred: $message")
}
}
is Resource.Loading -> {
showProgressBarSendMessage()
}
is Resource.Success -> {
response.data?.let {
it.metadata?.let { metadata ->
metadata.name?.let { imageName ->
val content = imageName.dropLast(4)
viewModel.sendMessage(
POSTMessage(content, media = true),
POSTLastMessage(content, media = true, msgRead = true),
chatID,
receiverID)
}
}
}
}
}
}
username.observe(viewLifecycleOwner) { response ->
when(response) {
is Resource.Error -> {
response.message?.let { message ->
Log.e(TAG, "An error occurred: $message")
}
}
is Resource.Loading -> { /* TODO: some loading animation */ }
is Resource.Success -> {
response.data?.let { username ->
binding.headlineText.text = username
}
}
}
}
sendMessage.observe(viewLifecycleOwner) { response ->
when(response) {
is Resource.Error -> {
response.message?.let { message ->
Log.e(TAG, "An error occurred: $message")
}
hideProgressBarSendMessage()
}
is Resource.Loading -> {
showProgressBarSendMessage()
}
is Resource.Success -> {}
}
}
setMessageRead.observe(viewLifecycleOwner) { response ->
when(response) {
is Resource.Error -> {
response.message?.let { message ->
Log.e(TAG, "An error occurred: $message")
}
}
is Resource.Loading -> {}
is Resource.Success -> {}
}
}
}
My Resource class looks like this:
sealed class Resource<T>(
val data: T? = null,
val message: String? = null) {
class Success<T>(data: T) : Resource<T>(data)
class Error<T>(message: String, data: T? = null) : Resource<T>(data, message)
class Loading<T> : Resource<T>()
}
If I changed it like this:
sealed interface Resource<T> {
class Success<T>(val data: T) : Resource<T>
class Error<T>(val message: String) : Resource<T>
class Loading<T> : Resource<T>
}
Then I have to modify methods in my ViewModel as follows:
//old Resource
fun getChats() = viewModelScope.launch {
chats.postValue(Resource.Loading())
homeRepository.getChats().collect() {
it.data?.let { getChats ->
setChatIDs(getChats)
getUnreadMessages(getChats)
}
chats.postValue(it)
}
}
//new Resource
private fun getChats() = viewModelScope.launch {
chats.postValue(Resource.Loading())
homeRepository.getChats().collect() {
if(it is Resource.Success) {
val data = (it as Resource.Success).data
setChatIDs(data)
getUnreadMessages(data)
}
chats.postValue(it)
}
}
That would make my Oberver methods more readable, but I'm not sure if that would be bad practise in the ViewModel.
答案1
得分: 1
你不能真正将这些清晰地分成类,因为它正在与 Fragment 内部进行交互。
但是,从查看您的代码并看到所有这些 ?.let
,我认为您需要更好的密封类/接口实现。
我猜想您的密封类看起来像这样:
sealed class Resource(
val data: String? = null,
val message: String? = null
) {
object Loading: Resource()
class Success(data: String): Resource(data = data)
class Error(message: String): Resource(message = message)
}
但实际上应该像这样:
sealed interface Resource {
object Loading: Resource
data class Success(val data: String): Resource
data class Error(val message: String): Resource
}
这样,您的 Success 类中就不会有一个无用的 message
属性,而 Error 类中也不会有一个无用的 data
属性。但更重要的是,message
和 data
不是可空的,因为它们只在相关的类中使用。
然后,您就不需要使用所有那些使您的代码变得很难看的 ?.let
调用了。
除此之外,您可以定义:
private fun Resource.Error.log() = Log.e(TAG, "An error occurred: $message")
并使用它来避免代码重复。
在您的 when
分支中,当只有一个函数调用时,您可以将其更改为没有括号 { }
包围的一行代码。例如:
when (response) {
// ...
is Resource.Loading -> showProgressBarSendMessage()
//...
}
英文:
You can't really break these out into classes cleanly because it's working with Fragment internals.
But, from looking at your code and seeing all those ?.let
s, I think you need a better sealed class/interface implementation.
I'm guessing your sealed class looks something like this:
sealed class Resource(
val data: String? = null,
val message: String? = null
) {
object Loading: Resource()
class Success(data: String): Resource(data = data)
class Error(message: String): Resource(message = message)
}
when it should really look like:
sealed interface Resource {
object Loading: Resource
data class Success(val data: String): Resource
data class Error(val message: String): Resource
}
This way, you don't have a useless message
property in your Success class or a useless data
property in your Error class. But more importantly, message
and data
are not nullable because they don't need to be--they are only used in the classes where they are relevant.
Then you won't need to use all those ?.let
calls, which are making your code really ugly.
Other than that, you could define:
private fun Resource.Error.log() = Log.e(TAG, "An error occurred: $message")
and use that to avoid the code duplication.
And in your when
branches, when there is only a single function call, you can change it to a one-liner with no brackets { }
surrounding it. For example:
when (response) {
// ...
is Resource.Loading -> showProgressBarSendMessage()
//...
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论