Назад к вопросам
Middle+
75
questionbank

Как вы планируете использовать паттерн Presentation Layer в своем проекте?

Sobes Copilot

Получайте ответы в реальном времени

Ответ от нейросети

sobes.tech AI

Presentation Layer (слой представления) будет отвечать за отображение данных пользователю и обработку его действий. Он будет состоять из UI-элементов (Activity, Fragment, Composables), Presenters/ViewModels и адаптеров.

Основные принципы использования:

  • Разделение ответственности: UI отображает данные, а Presenter/ViewModel управляет логикой отображения, взаимодействуя с доменом.
  • Тестируемость: Логика представления вынесена в Presenter/ViewModel, что упрощает юнит-тестирование.
  • Независимость от Android API: Presenter/ViewModel в идеале не должен содержать ссылок на Android SDK (Context, View и т.д.), используя интерфейсы и DI.

Пример структуры с использованием Jetpack Compose и ViewModel:

com.example.myapp.presentation
├── common // Общие компоненты UI
├── home // Feature Home
│   ├── HomeViewModel.kt // ViewModel для экрана Home
│   └── HomeScreen.kt // Composable для экрана Home
├── details // Feature Details
│   ├── DetailsViewModel.kt
│   └── DetailsScreen.kt
└── navigation // Навигация между экранами

В случае использования XML/Fragments, структура будет аналогичной, но вместо Composable будут Fragment и соответствующие XML-макеты. Presenter или ViewModel будет работать с View через интерфейс или Data Binding.

Взаимодействие между слоями:

  • UI (Activity/Fragment/Composable) -> ViewModel: отправка действий пользователя (клики, ввод).
  • ViewModel -> Domain: получение/отправка данных.
  • Domain -> ViewModel: предоставление данных/результатов операций.
  • ViewModel -> UI: обновление состояния UI (через StateFlow, LiveData или другие механизмы наблюдения).

Пример использования ViewModel в Composable:

// HomeScreen.kt
@Composable
fun HomeScreen(
    viewModel: HomeViewModel = hiltViewModel() // DI для ViewModel
) {
    val uiState by viewModel.uiState.collectAsState() // Наблюдение за состоянием UI

    when (uiState) {
        is HomeUiState.Loading -> LoadingIndicator()
        is HomeUiState.Success -> {
            val data = (uiState as HomeUiState.Success).data
            // Отображение данных
        }
        is HomeUiState.Error -> {
            val message = (uiState as HomeUiState.Error).message
            ErrorView(message)
        }
    }
}

// HomeViewModel.kt
@HiltViewModel
class HomeViewModel @Inject constructor(
    private val getItemsUseCase: GetItemsUseCase // UseCase из Domain слоя
) : ViewModel() {

    private val _uiState = MutableStateFlow<HomeUiState>(HomeUiState.Loading)
    val uiState: StateFlow<HomeUiState> = _uiState.asStateFlow()

    init {
        loadItems()
    }

    private fun loadItems() {
        viewModelScope.launch {
            getItemsUseCase.execute() // Вызов UseCase
                .onStart { _uiState.value = HomeUiState.Loading }
                .catch { e -> _uiState.value = HomeUiState.Error(e.message ?: "Unknown error") }
                .collect { items -> _uiState.value = HomeUiState.Success(items) }
        }
    }
}

// HomeUiState.kt
sealed class HomeUiState {
    object Loading : HomeUiState()
    data class Success(val data: List<Item>) : HomeUiState()
    data class Error(val message: String) : HomeUiState()
}

Такой подход обеспечивает четкое разделение слоев, улучшает тестируемость и масштабируемость проекта.