Android开发中的前五个代码异味:Jetpack Compose UI和MVVM

Android开发中的前五个代码异味:Jetpack Compose UI和MVVM

learn-kotlin

代码异味是指软件代码中潜在问题的指标,可能并不一定是错误,但可能会导致问题,如增加维护复杂性、降低性能或降低可读性。我们将探讨Android开发中的前五个代码异味,其中包括使用Jetpack Compose UI和Model-View-ViewModel(MVVM)架构的示例。

1. 上帝对象或上帝类:

上帝对象或上帝类是指试图做太多事情的单个类,违反了单一职责原则。在Android开发中,这可能会使代码更难理解、维护和修改。

示例:

我们将以一个充当“上帝对象”的ViewModel为例,然后使用存储库模式进行重构。

上帝对象ViewModel示例:

class GodObjectViewModel : ViewModel() {
    private val apiService = ApiService()
    private val database = AppDatabase.getInstance()

    // LiveData to expose data to the UI
    private val _data = MutableLiveData<List<DataItem>>()
    val data: LiveData<List<DataItem>> = _data

    init {
        loadData()
    }

    private fun loadData() {
        viewModelScope.launch {
            // Fetch data from API
            val apiData = apiService.getData()

            // Save data to database
            database.dataDao().insertAll(apiData)

            // Load data from database
            val dbData = database.dataDao().getAll()

            // Transform data
            val transformedData = transformData(dbData)

            // Update LiveData
            _data.value = transformedData
        }
    }

    private fun transformData(input: List<DataItem>): List<DataItem> {
        // Perform some transformations on the data
        // ...
        return transformedData
    }
}

使用仓库模式重构后的 ViewModel:

class RefactoredViewModel(private val dataRepository: DataRepository) : ViewModel() {
    // LiveData to expose data to the UI
    private val _data = MutableLiveData<List<DataItem>>()
    val data: LiveData<List<DataItem>> = _data

    init {
        loadData()
    }

    private fun loadData() {
        viewModelScope.launch {
            // Fetch data using the repository
            val transformedData = dataRepository.fetchData()

            // Update LiveData
            _data.value = transformedData
        }
    }
}

为了改进代码,我们使用仓库模式对 ViewModel 进行了重构。我们创建了一个独立的 DataRepository 类,它负责处理 API 调用、数据库操作和数据转换的职责。然后,重构后的 ViewModel 被简化为仅专注于通过委托数据获取任务到 DataRepository 来向 UI 公开数据。

2. 深层继承层次结构:

过度使用继承可能导致代码复杂且难以维护。在适当的情况下,应优先考虑组合而非继承。

示例:

使用 Kotlin 和 Jetpack Compose,展示了一个深层的继承层次结构,然后使用组合对其进行了重构。

abstract class BaseComposable {
    abstract fun DrawScope.draw()
}

class FirstLevelComposable : BaseComposable() {
    override fun DrawScope.draw() {
        // Implement some functionality
    }
}

class SecondLevelComposable : FirstLevelComposable() {
    override fun DrawScope.draw() {
        super.draw()
        // Add more functionality
    }
}

class ThirdLevelComposable : SecondLevelComposable() {
    override fun DrawScope.draw() {
        super.draw()
        // Add even more functionality
    }
}

@Composable
fun DeepInheritanceHierarchyExample() {
    val thirdLevelComposable = ThirdLevelComposable()

    Canvas(modifier = Modifier.fillMaxSize()) {
        thirdLevelComposable.draw()
    }
}

使用组合进行重构:

@Composable
fun BaseComposable() {
    // Implement some functionality
}

@Composable
fun FirstLevelComposable() {
    BaseComposable()
    // Add more functionality
}

@Composable
fun SecondLevelComposable() {
    FirstLevelComposable()
    // Add even more functionality
}

@Composable
fun CompositionExample() {
    Canvas(modifier = Modifier.fillMaxSize()) {
        SecondLevelComposable()
    }
}

在重构的示例中,我们使用组合替换了深度继承层次结构,使代码更易于理解和维护。我们创建了小型可重用的 Composable 函数,而不是从多个父类继承,可以组合这些函数来实现所需的功能。这种方法更加灵活,并遵循 Jetpack Compose 的原则。

3. 硬编码值或“魔法数字”:

硬编码值可能会使代码不够灵活,难以维护。使用常量或资源文件存储可能会发生更改的值。
示例:
在 Jetpack Compose 中,我们不使用像 colors.xml、dimens.xml 和 strings.xml 这样的 XML 文件。相反,我们在 Kotlin 文件中定义这些资源。以下是一个使用硬编码值的示例,并对其进行重构以使用常量:

硬编码代码示例:

import androidx.compose.foundation.layout.padding
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

@Composable
fun HardcodedValuesExample() {
    Text(
        text = "Hello, World!",
        color = Color(0xFF3F51B5),
        modifier = Modifier.padding(16.dp)
    )
}

使用常量进行重构:
创建一个专门的文件,例如Theme.kt

import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

object Constants {
    val primaryColor = Color(0xFF3F51B5)
    val defaultPadding = 16.dp
}

更新 HardcodedValuesExample 使用常量:

import androidx.compose.foundation.layout.padding
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import Constants.primaryColor
import Constants.defaultPadding

@Composable
fun RefactoredValuesExample() {
    Text(
        text = "Hello, World!",
        color = primaryColor,
        modifier = Modifier.padding(defaultPadding)
    )
}

在重构的示例中,我们使用在专门的文件中定义的常量来代替硬编码的值。这使得代码更易于维护和更新。对于文本字符串,您可以使用类似的方法,在单独的文件中定义字符串常量,并在您的 Composables 中使用它们。

4. 长方法或类:

长方法或类难以理解和维护。将它们拆分成更小、更集中的功能单元。
示例:
使用 Kotlin 和 Jetpack Compose,我们有一个 ViewModel 类,其中包含一个长方法,处理数据转换和业务逻辑。我们将通过将方法拆分为更小、更专注的函数来进行重构。
长方法示例:

class LongMethodViewModel : ViewModel() {
    // ...

    private fun processData(data: List<DataItem>): List<ProcessedDataItem> {
        // Step 1: Filter the data
        val filteredData = data.filter { item ->
            item.isActive && item.value > 10
        }

        // Step 2: Transform the data
        val transformedData = filteredData.map { item ->
            ProcessedDataItem(
                id = item.id,
                displayName = "${item.name} (${item.value})",
                important = item.value > 50
            )
        }

        // Step 3: Sort the data
        val sortedData = transformedData.sortedByDescending { item ->
            item.important
        }

        return sortedData
    }
}

重构成更小更专一的代码:

class RefactoredViewModel : ViewModel() {
    // ...

    private fun processData(data: List<DataItem>): List<ProcessedDataItem> {
        return data.filter(::filterData)
            .map(::transformData)
            .sortedByDescending(::sortData)
    }

    private fun filterData(item: DataItem): Boolean {
        return item.isActive && item.value > 10
    }

    private fun transformData(item: DataItem): ProcessedDataItem {
        return ProcessedDataItem(
            id = item.id,
            displayName = "${item.name} (${item.value})",
            important = item.value > 50
        )
    }

    private fun sortData(item: ProcessedDataItem): Boolean {
        return item.important
    }
}

在重构的例子中,我们将长的processData方法分解为更小、更专注的函数:filterData、transformData和sortData。这使得代码更易于理解、测试和维护。

5. 强耦合:

高度依赖彼此的类或组件可能会使修改或测试代码变得困难。通过设计具有良好定义接口和最小依赖关系的组件来实现松耦合。
例子:
使用Kotlin和Jetpack Compose,ViewModel和Composable函数之间存在强耦合。我们将使用StateFlow重构它以实现松耦合。
强耦合例子:

class TightCouplingViewModel : ViewModel() {
    val data = mutableStateOf(listOf<DataItem>())

    init {
        loadData()
    }

    private fun loadData() {
        // Load data...
        data.value = loadedData
    }
}

@Composable
fun TightCouplingExample(viewModel: TightCouplingViewModel) {
    val data = viewModel.data.value
    LazyColumn {
        items(data) { item ->
            // Display data item...
        }
    }
}

使用松散耦合进行重构:
更新 ViewModel 以使用 StateFlow:

import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow

class LooseCouplingViewModel : ViewModel() {
    private val _data = MutableStateFlow(listOf<DataItem>())
    val data: StateFlow<List<DataItem>> = _data

    init {
        loadData()
    }

    private fun loadData() {
        // Load data...
        _data.value = loadedData
    }
}

更新 Composable 函数以观察来自 ViewModel 的数据:

import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue

@Composable
fun LooseCouplingExample(viewModel: LooseCouplingViewModel) {
    val data by viewModel.data.collectAsState()
    LazyColumn {
        items(data) { item ->
            // Display data item...
        }
    }
}

在重构的示例中,我们通过使用StateFlow来观察和响应ViewModel中数据的变化,实现了ViewModel和Composable函数之间的松耦合。这种方法使得修改或测试代码更容易,因为ViewModel和UI组件不会直接引用彼此。

结论

总之,在Android开发中解决五大代码坏味道有助于创建更清晰、更高效、更高质量的代码库。通过遵循Jetpack Compose UI和MVVM架构的最佳实践,开发人员可以创建具有模块化、可维护和可测试性的应用程序。

  1. 通过将大型类分解为更小、更集中的功能单元,实现避免过多使用god对象,并实现存储库模式。
  2. 通过组合取代深层继承层次结构,创建更小、可重用的可组合项或混合项,以实现所需的功能。
  3. 通过将硬编码的值或“神奇数字”替换为常量或资源文件,使代码更具灵活性和可维护性。
  4. 通过将长方法或类分解为更小、更集中的功能单元,使其更易于理解、测试和维护。
  5. 通过设计具有良好定义接口和最小依赖关系的组件、使用LiveData或StateFlow来观察和响应ViewModel中的数据变化,并避免ViewModel和UI组件之间的直接引用,实现松耦合。
    通过解决这些代码坏味道,您可以增强开发过程,改善可维护性,并创建提供优秀用户体验的Android应用程序。

参考

https://medium.com/@fauzisho/top-5-code-smells-in-android-development-jetpack-compose-ui-and-mvvm-e3934ddbc14c

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Calvin880828

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值