← Назад к вопросам

С какими работал MV паттернами кроме MVVM

1.2 Junior🔥 81 комментариев
#Архитектура и паттерны#Сетевое взаимодействие

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Опыт работы с архитектурными паттернами для Android

В дополнение к MVVM (Model-View-ViewModel), в моей практике на проектах различного масштаба активно использовались и другие архитектурные подходы, каждый из которых был применен в зависимости от конкретных требований проекта, его сложности и исторического контекста. Вот основные паттерны, с которыми я работал:

MVC (Model-View-Controller)

Это классический паттерн, который часто встречался в ранних Android проектах и некоторых специфических случаях.

  • Структура: Активность или Фрагмент выступает как View, бизнес-логика находится в Model, а Controller (часто сама Активность) связывает их, обрабатывая пользовательский ввод и обновляя View.
  • Контекст использования: Часто использовался в небольших приложениях или модулях, где не требовалась сложная реактивность. Также встречался при интеграции с legacy кодом или в проектах, где основная логика была на стороне сервера, а клиентская часть относительно проста.
  • Пример упрощенной реализации:
// Model
class UserModel(val name: String, val email: String)

// Controller & View (Activity как Controller и часть View)
class UserActivity : AppCompatActivity() {
    private lateinit var userNameTextView: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_user)

        userNameTextView = findViewById(R.id.user_name_text_view)

        // Контроллер обрабатывает "загрузку данных" и обновляет View
        val user = loadUserData() // Получаем Model
        updateView(user) // Обновляем View на основе Model
    }

    private fun loadUserData(): UserModel {
        // Здесь может быть запрос к сети, базе данных или просто заглушка
        return UserModel("Иван Иванов", "ivan@example.com")
    }

    private fun updateView(user: UserModel) {
        userNameTextView.text = user.name
    }
}
  • Проблемы: Основная сложность в чистом MVC для Android — сильное смешение ответственности в Активности/Фрагменте (они становятся и View, и Controller), что приводит к "жирным" классам, сложностям в тестировании бизнес-логики и управлении жизненным циклом.

MVP (Model-View-Presenter)

Этот паттерн был чрезвычайно популярен до широкого внедрения MVVM и Android Architecture Components. Он решал многие проблемы MVC, четко разделяя ответственности.

  • Структура: View (Активность/Фрагмент) отвечает только за отображение данных и обработку пользовательских событий. Presenter содержит всю презентационную логику: получает данные от Model, преобразует их для отображения и передает во View. Model остается хранителем данных и бизнес-логики.
  • Ключевое отличие от MVVM: Presenter имеет прямую ссылку на View (обычно через интерфейс) и активно управляет ей, вызывая ее методы. В MVVM View наблюдает за изменениями в ViewModel через механизмы наблюдателя (например, LiveData, StateFlow).
  • Контекст использования: Широко применялся в средних и крупных проектах до эпохи Jetpack. Особенно эффективен там, где требовалась четкая тестируемость презентационной логики (Presenter легко тестировать в isolation) и более явный контроль над потоком данных, чем в реактивном MVVM.
  • Пример реализации с интерфейсом View:
// Contract (описывает взаимодействие)
interface UserViewContract {
    fun showUserName(name: String)
    fun showError(message: String)
}

// Presenter
class UserPresenter(private val view: UserViewContract) {
    fun loadUser() {
        try {
            val userModel = UserRepository.getUser() // Получаем Model
            view.showUserName(userModel.name) // Управляем View
        } catch (e: Exception) {
            view.showError(e.message ?: "Ошибка")
        }
    }
}

// View (Activity реализует контракт)
class UserActivity : AppCompatActivity(), UserViewContract {
    private lateinit var presenter: UserPresenter
    private lateinit var userNameTextView: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_user)
        userNameTextView = findViewById(R.id.user_name_text_view)

        presenter = UserPresenter(this) // Presenter получает ссылку на View
        presenter.loadUser()
    }

    override fun showUserName(name: String) {
        userNameTextView.text = name
    }

    override fun showError(message: String) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
    }
}

MVI (Model-View-Intent)

Это более современный и функционально-ориентированный паттерн, который я использовал в проектах, требующих высокой степени predictability и однозначности состояния UI.

  • Структура: Пользовательские действия формулируются как Intent. View имеет единственное, immutable State. ViewModel (или аналогичный компонент) принимает Intent, обрабатывает его (часто с использованием Reducer), и производит новое State, которое передается View для отображения. Model здесь часто представляет собой источник данных.
  • Контекст использования: Идеально подходит для сложных UI с множеством взаимосвязанных состояний (например, формы с валидацией, многошаговые процессы). Гарантирует, что состояние интерфейса всегда предсказуемо и детерминировано. Часто реализуется с помощью Kotlin Coroutines Flow или RxJava.
  • Пример концептуального состояния:
// Immutable State
data class UserScreenState(
    val userName: String = "",
    val isLoading: Boolean = false,
    val errorMessage: String? = null
)

// Intent (или Action)
sealed class UserIntent {
    object LoadUser : UserIntent()
    object Retry : UserIntent()
}

// В ViewModel происходит обработка:
class UserViewModel : ViewModel() {
    private val _state = MutableStateFlow(UserScreenState())
    val state: StateFlow<UserScreenState> = _state.asStateFlow()

    fun processIntent(intent: UserIntent) {
        when (intent) {
            UserIntent.LoadUser -> {
                _state.update { it.copy(isLoading = true) }
                // Загрузка данных, обновление состояния...
                _state.update { it.copy(isLoading = false, userName = "Новое имя") }
            }
            UserIntent.Retry -> {
                // Обработка повторной попытки
            }
        }
    }
}

Выбор паттерна

Выбор между MVP, MVVM, MVI и другими зависит от:

  1. Сложности проекта: MVP хорош для явного контроля, MVVM — для реактивной связки с Jetpack, MVI — для сложных состояний.
  2. Команды и инструментов: Наличие опыта с RxJava, Coroutines Flow, Jetpack Components.
  3. Требований к тестируемости: Все эти паттерны улучшают тестируемость по сравнению с MVC, но по-разному (MVP — unit-тесты Presenter, MVVM/MVI — тесты ViewModel).
  4. Исторического контекста: Переход с legacy MVP на MVVM или внедрение MVI в новый проект.

В современной разработке под Android MVVM с компонентами Jetpack (LiveData/Flow, ViewModel, DataBinding) часто является стандартом де-факто для новых проектов, но глубокое понимание MVP и MVI позволяет выбирать более оптимальную архитектуру для специфических задач и создавать более robust, тестируемый и поддерживаемый код.