Как вы планируете использовать паттерн 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()
}
Такой подход обеспечивает четкое разделение слоев, улучшает тестируемость и масштабируемость проекта.