Обзор основных архитектурных паттернов проектирования мобильных приложений
Привет! Я - Илья Воскобойников, Android-разработчик департамента автоматизации бизнеса ГК "Технологии Надежности". Сегодня я расскажу про основные архитектурные паттерны проектирования мобильных приложений. На живых примерах разберемся, когда нужно использовать тот или иной подход, какие у каждого из них есть плюсы и минусы. Погнали!
Шаблоны проектирования — это типичные решения часто встречающихся проблем при проектировании программного обеспечения. Они похожи на готовые чертежи, которые вы можете настроить для решения повторяющейся проблемы проектирования в вашем коде.
Большинство паттернов описываются очень формально, поэтому люди могут воспроизводить их во многих контекстах. Вот разделы, которые обычно присутствуют в описании паттерна:
– цель шаблона кратко описывает как проблему, так и решение;
– мотивация дополнительно объясняет проблему и решение;
– структура классов показывает каждую часть шаблона и то, как они связаны.
Далее будем рассматривать пример реализации паттернов на прототипе приложения с авторизацией, полный листинг программ: https://gitlab.com/500a5/example-architectures-mvc-mvp-mvvm
1. The Model—View—Controller (MVC) Pattern
Цель шаблона:
Разделить приложение на три компонента (Model, View, Controller) (рисунок 1), для упрощения разработки и поддержки.
Мотивация:
– упрощение разработки: позволяет командам работать над различными компонентами независимо;
– поддерживаемость: четкая структура облегчает обновления и исправления;
– тестируемость: легче тестировать отдельные компоненты приложения.
Структура:
Model — это бизнес-логика и состояние данных. Получение и обработка данных, связь с контроллером, взаимодействие с базой данных, иногда обновление представлений.
View — то, что мы видим. Пользовательский интерфейс может быть реализован с использованием HTML/CSS/XML, UIKit, Compose, SwiftUI. Интерфейс общается с контроллером и иногда взаимодействует с моделью. Он передаёт некоторые динамические представления через контроллер.
Controller — Уровень, который содержит основную логику. Он взаимодействует с представлением и моделью. Controller принимает пользовательский ввод из служб просмотра/REST. Обрабатывает запрос, получает данные из модели и передаёт в представление.
Преимущества:
– разделяет бизнес-логику в модели;
– поддерживает асинхронные методы;
– модификация не затрагивает всю модель;
– более быстрый процесс разработки.
Недостатки:
– из-за большого количества кода в контроллере он может быть захламлен;
– мешает модульному тестированию.
Пример реализации:
В качестве View выступает activity_mvc.xml.
Model, которая ничего не знает о view, представляет интерфейс AuthRepository.kt и его реализацию AuthRepositoryImpl.kt.
В качестве контроллера выступает MvcActivity.kt. Этот класс установит связь между представлением и моделью. Данные, предоставленные Моделью, будут использоваться View, и в действие будут внесены соответствующие изменения.
Скриншоты иерархии примера на рисунке 2 и рисунке 3:
В небольших проектах или прототипах часто используется MVC. Он позволяет быстро начать разработку благодаря своей простой структуре. Однако в больших приложениях я обнаружил, что взаимодействие между компонентами может стать сложным. Логика контроллера может разрастись, что затрудняет поддержку кода. На практике это уже можно считать устаревшим подходом, и в продакшене его вряд ли можно встретить. Следующей ступенью после MVC можно считать MVP, который мы рассмотрим далее. Он решает большинство проблем, которые были в MVC.
2. The Model—View—Presenter (MVP) Pattern
Цель шаблона:
Изолировать логику представления от интерфейса для упрощения тестирования и поддержки.
Мотивация:
– лучшая тестируемость: presenter можно тестировать без необходимости взаимодействия с UI.
– чистота кода: логика представления отделена от кода интерфейса, что упрощает изменения.
– упрощение сложных сценариев: presenter может управлять сложной логикой взаимодействия пользователя.
Структура:
Паттерн MVP – является архитектурным шаблоном проектирования, который используется для создания пользовательских интерфейсов. Этот паттерн разделяет логику приложения на три основные части:
Model — Это бизнес-логика и состояние данных. Получая и обрабатывая данные, общается с presenter, взаимодействует с базой данных. Model не взаимодействует с view.
View — Состоит из пользовательского интерфейса, активности и фрагмента. View взаимодействует с presenter.
Presenter — Представляет данные из модели. Управляет всем поведением, которое хочет отобразить из приложения. Presenter сообщает view, что делать. Любое взаимодействие между моделью и представлением обрабатывается presenter.
В схеме (рисунок 4) MVP View и Presenter тесно связаны и ссылаются друг на друга.
Преимущества:
– делает view абстрактной, так что бы ее можно было легко поменять;
– переиспользование View и Presenter;
– код более читабельный и удобный в сопровождении;
– простое тестирование, так как бизнес-логика отделена от пользовательского интерфейса.
Недостатки:
– тесная связь между View и Presenter;
– огромное количество интерфейсов для взаимодействия между слоями;
– размер кода довольно избыточен.
Пример реализации:
Для установления связи между View-Presenter и Presenter-Model необходим интерфейс. Этот класс интерфейса будет содержать все абстрактные методы, которые будут определены позже в классе View, Model и Presenter. LoginContract.kt
Presenter - методы этого класса содержат основную бизнес-логику, которая будет решать, что отображать и как отображать. Он запускает класс View для внесения необходимых изменений в пользовательский интерфейс. LoginPresenterImpl.kt
Класс View отвечает за обновление пользовательского интерфейса в соответствии с изменениями, вызванными уровнем Presenter. Данные, предоставленные моделью, будут использоваться View, и в действие будут внесены соответствующие изменения. MvpActivity.kt
В качестве Model выступают AuthRepository.kt, AuthRepositoryIml.kt
Скриншоты иерархии примера на рисунке 5:
MVP стал для меня наиболее подходящим выбором для разработки средних приложений. Благодаря ему можно изолировать логику представления от пользовательского интерфейса, что значительно упрощает процесс тестирования Этот шаблон стал очень популярным и до сих пор активно применяется в разработке средних приложений, несмотря на появление более современных шаблонов, таких как MVVM и других.
3. The Model—View—ViewModel (MVVM) Pattern
Цель шаблона:
Обеспечить двустороннюю привязку данных между View и ViewModel для упрощения взаимодействия.
Мотивация:
– двусторонняя привязка: Изменения в ViewModel автоматически отражаются в View, что упрощает управление состоянием.
– тестируемость: ViewModel можно тестировать независимо от UI.
– удобство разработки: Использование библиотек (например, LiveData) упрощает создание интерактивных интерфейсов.
Структура:
Этот шаблон основан на MVC и MVP, который пытается более четко отделить разработку пользовательского интерфейса от разработки бизнес-логики и поведения в приложении (рисунок 6).
Этот паттерн разделяет логику приложения на три основные части:
Model — Содержит бизнес-логику, локальный и удаленный источник данных и репозиторий. Репозиторий: взаимодействует с локальными или удаленными источниками данных в соответствии с запросом от ViewModel.
View — Взаимодействует только с пользователем, без бизнес-логики. Цель этого слоя — информировать ViewModel о действиях пользователя. View наблюдает за ViewModel и не содержит никакой логики приложения.
ViewModel — Большая часть логики пользовательского интерфейса сосредоточена здесь. Это мост между представлением и бизнес-логикой. У него нет никакой привязки, какое представление должно его использовать. Поскольку viewModel не имеет прямой ссылки на представление, она хорошо тестируется и имеет слабую связанность. Обновление пользовательского интерфейса реализуется через компоненты‐наблюдатели, когда данные изменяются viewModel сигнализирует об этом своим подписчикам.
Шаблоны MVVM и MVP очень похожи, поскольку оба эффективно абстрагируются от состояния и поведения уровня представления. В MVVM представления могут привязываться к потокам данных, предоставляемым ViewModel.
В схеме MVVM View информирует ViewModel о различных действиях. Представление имеет ссылку на ViewModel, в то время как ViewModel не имеет информации о представлении. Связь «многие к одному», которая существует между View и ViewModel и MVVM, поддерживает двустороннюю привязку данных между ними.
Преимущества:
– Разделение логики приложения и представления
– Гибкость и переиспользование
– Связывание данных
– Упрощение тестирования
Недостатки:
– Увеличение объема кода
Пример реализации:
В качестве Model выступает AuthRepository.kt и AuthRepositoryImpl.kt
ViewModel содержит все методы, которые необходимо вызывать в макете приложения. Он преобразует данные в потоки и уведомляет View. LoginViewModel.kt
В качестве view выступает MvvmActivity.kt.
Скриншоты иерархии примера на рисунке 7:
В настоящее время архитектурный паттерн MVVM (Model-View-ViewModel) является одним из самых популярных выборов для разработки мобильных приложений. Это связано с тем, что MVVM предоставляет отличную модульную тестируемость и поддерживает модульный принцип, что позволяет разработчикам создавать более гибкие и масштабируемые приложения. Кроме того, компания Google активно рекомендует использовать паттерн MVVM для разработки приложений под Android, предоставляя различные библиотеки и инструменты для его реализации, такие как Architecture Components и Jetpack. Этот патттен чаще других можно встретить в проде.
Вывод
Данный полученные исходя из анализа всех рассматриваемых паттернов архитектур приложения приведены в таблице 1.
Выбор архитектурного паттерна для разработки мобильных приложений является важным этапом, который влияет на гибкость, тестируемость и масштабируемость. Рассмотренные в статье паттерны — MVC, MVP и MVVM — имеют свои преимущества и недостатки, которые следует учитывать в зависимости от масштаба проекта и требований к его поддержке.
MVC является классическим паттерном, который подходит для небольших проектов и прототипов, так как позволяет быстро начать разработку благодаря своей простой структуре. Однако, по мере роста приложения, код контроллера может стать захламлённым, что затруднит поддержку и тестирование. MVP, в свою очередь, предлагает лучшее разделение логики, что упрощает тестирование и делает код более управляемым. Тем не менее, он требует больше усилий для создания дополнительных интерфейсов, что может усложнить архитектуру. MVVM, рекомендованный Google для разработки Android-приложений, предлагает наилучшие возможности для двусторонней привязки данных, модульного тестирования и разделения логики. Этот паттерн идеально подходит для крупных и сложных приложений, где требуется высокая гибкость и поддерживаемость.
Следовательно, выбор архитектурного паттерна зависит от анализа конкретных требований проекта, его масштабируемости, необходимости тестирования и предпочтений команды разработчиков.