Является ли логика принятия решений внутри функций Android Composable плохой идеей?

Что касается Android Jetpack-Compose, то обычной практикой является размещение логических частей внутри модели представления. Вот почему я использую состояния пользовательского интерфейса, чтобы абстрагировать свою бизнес-логику от того, что я показываю. Но куда мне поместить необходимую «логику визуального решения».

Этот вопрос задуман как общий вопрос о лучших практиках архитектуры в Android Compose.

Visual-decision-logic := что должно отображаться в зависимости от состояния пользовательского интерфейса

Пример: использование простой логики внутри вложенных компонуемых объектов

/**
 * this is the topmost Composable which is called by the Activity
 */
@Composable
fun DashboardScreen(dashboardViewModel: DashboardViewModel = hiltViewModel()) {
    val dashboardLayoutState: DashboardLayoutState? by
    dashboardViewModel.dashboardLayoutState.collectAsStateWithLifecycle()

    val isLongpressHintDisplayed: Boolean by
    dashboardViewModel.isLongpressHintDisplayed.collectAsStateWithLifecycle(
        initialValue = false
    )

    DashboardComposable(
        isLongpressHintDisplayed,
        dashboardLayoutState
    )
}

@Composable
private fun DashboardComposable(
    isHintDisplayed: Boolean,
    dashboardLayoutState: DashboardLayoutState?
) {
    val pagerState = rememberPagerState(pageCount = { dashboardLayoutState.pages.size })
    HorizontalPager(
        state = pagerState
    ) { index ->

        // question is about this if statement/logic
        // also a longer when-statement could be here instead
        if (isHintDisplayed) {
            HintComposable()
        } else {
            DashboardPageComposable(
                pageIndex = index
            )
        }
    }
}

Плюсы: прямое кодирование, локальные переменные, такие как pagerState, сохраняются и используются там, где это необходимо. минусы: вложенные компоненты не являются «хорошими» для тестирования из-за «скрытых» путей и могут ухудшаться в больших деревьях компоновки.

Отвечает ли это на ваш вопрос? Developer.android.com/topic/architecture/ui-layer#logic-type‌​s

Leviathan 18.06.2024 10:33
1
1
65
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Совершенно нормально использовать if-else в Composable, чтобы определить, какие Composables отображать в зависимости от состояния. ViewModel должна инкапсулировать бизнес-логику как

  • подключение к хранилищам данных
  • предоставление данных в переменных состояния, которые могут использоваться Composables
  • предлагая функции для изменения переменных состояния

Если вам нужен какой-то способ условного отображения Composable, этого можно добиться только с помощью if-else в Composable. Но если вместо этого вы используете if-else в Composable для изменения переменных состояния на основе условий, это не будет хорошей практикой.

Взгляните на Репозиторий образцов Jetpack Compose. Это официальный репозиторий, поддерживаемый Google и, таким образом, включающий в себя лучшие практики. Вы найдете множество фрагментов if-else в Composables при просмотре исходного кода:

Разговоры.кт

if (index == messages.size - 1) {
    item {
        DayHeader("20 Aug")
    }
} else if (index == 2) {
    item {
        DayHeader("Today")
    }
}

// ...

if (isFirstMessageByAuthor) {
    // Last bubble before next author
    Spacer(modifier = Modifier.height(8.dp))
} else {
    // Between bubbles
    Spacer(modifier = Modifier.height(4.dp))
}

ОтветитьAppBars.kt

if (searchResults.isNotEmpty()) {
    LazyColumn(
        modifier = Modifier.fillMaxWidth(),
        contentPadding = PaddingValues(16.dp),
        verticalArrangement = Arrangement.spacedBy(4.dp)
    ) {
        // ...
    }
} else if (query.isNotEmpty()) {
    Text(
        text = stringResource(id = R.string.no_item_found),
        modifier = Modifier.padding(16.dp)
    )
} else {
    Text(
        text = stringResource(id = R.string.no_search_history),
        modifier = Modifier.padding(16.dp)
    )
}

CraneHome.kt

При наличии более трех различных ветвей рассмотрите возможность использования конструкции Kotlin When для лучшей читаемости:

when (craneScreenValues[page]) {
    CraneScreen.Fly -> {
        suggestedDestinations?.let { destinations ->
            ExploreSection(
                // ...
            )
        }
    }

    CraneScreen.Sleep -> {
        ExploreSection(
            // ...
        )
    }

    CraneScreen.Eat -> {
        ExploreSection(
            // ...
        )
    }
}

Обратите внимание, что условия внутри блоков if и else if остаются довольно простыми. Если у вас возникнут очень сложные условные выражения, включающие множество различных переменных состояния из ViewModel, вам может потребоваться дополнительная инкапсуляция состояния в ViewModel путем введения отдельного класса UIState.

спасибо, что напомнили мне о странице примера Android :)

Marcel 18.06.2024 11:03

Один открытый вопрос: какая тестовая платформа/инструмент/настройка является хорошим выбором для разработки непокрытых ветвей в составных объектах?

Marcel 18.06.2024 11:09

Я не уверен, что полностью понял ваш вопрос. Но если вы хотите обнаружить, что вы не рассмотрели возможную ветвь if-else, вы можете просто использовать обычное тестирование Jetpack Compose: Developer.android.com/develop/ui/compose/testing Обычно такие ошибки Тогда это совершенно очевидно, так как на экране будет отсутствовать составной объект или будет отображаться неправильный составной объект. Платформа тестирования может проверять видимость Composables и, таким образом, улавливать это.

BenjyTec 18.06.2024 11:36

Другие вопросы по теме