Архитектурный патерн MVVM

Ранее мы рассмотрели такие патерны архитектуры как MVP MVC и теперь, время поговорить о таком патерне как MVVM. Несложно перейти на википедию и понять, что она расшифровывается как Model View ViewModel. Ровно так же как и MVP/MVC — модель, вью и что-то еще.

Давайте для начала поговорим о том, почему вообще все эти патерны существуют. Для простоты ограничимся платформой андроид (или любой другой мобильной). Вся суть разработки под мобильную платформу сводится к решению одной простой задачи — синхронизировать то, что видит пользователь с теми данными, которые он вводит или же получает извне. Собственно поэтому у нас и 3 понятия — модель данных, вью и чтобы как-то их вместе связать нам нужен третий компонент. В одном случае мы назовем его презентером, в другом вьюмоделью. В третьем… как собственно захотите. Суть от этого не поменяется.

Итак, начнем мы с того, чем по сути отличается MVVM от MVP. И это не только название третьего класса, нет. Здесь есть одна изюминка. Если посмотреть на страницу википедии, то там ясно обозначен один термин — связывание данных — a.k.a. DataBinding. В windows-подобных системах патерн MVVM имел больший успех именно из-за специфики платформы — очень удобное связывание данных с отображением. Т.е. каким-то образом задается правило по которому отображение связывается с данными. Например — пользовательский ввод сразу же влияет на некоторые данные и ровно так же изменение этих данных извне напрямую влияет на пользовательский интерфейс.

Почему же MVVM, скажем так, в последнее время заинтересовал Google и огромный процент андроид разработчиков? Для этого давайте вспомним некоторые особенности реализации MVP и такую забавную штуку как жизненный цикл компонентов андроид. Кто-то скажет, что под это дело было создано Moxy, который занимался именно актуализацией отображения при смене состояния отображения. Но, как вы все хорошо помните и понимаете, все это происходит очень больно и мучительно, ведь от такой страшной вещи как смерть и восстановления процесса никто не застрахован. И следственно этот редкий (на самом деле не такой уж и редкий) случай привносил и привносит в жизнь андроид разработчика немало счастья.

Плюс давайте вспомним, что для того, чтобы связать презентер с отображением мы писали отдельный интерфейс для вью и давали его на сьедение нашему презентеру. С одной стороны связь довольно слабая, но с другой она все равно существует. И если вы не фанат Moxy, то вам самостоятельно нужно разруливать все страшные события жизненного цикла. Только вот от одного события наша всеми любимая библиотека не сильно спасает — когда мы пытаемся поменять фрагмент в активти после того, как было вызвано сохранение состояния. Некоторые гениальные разработчики читая джавадоки решают сделать commitAllowStateLoss и верят что у них все замечательно. Но нет. Ладно, оставим эту боль и перейдем к божественному решению.

Как всем известно, в последнее время сообщество андроид разработчиков просит корпорацию добра иметь свое мнение и видение в разработке и поэтому отважные разработчики из Google представили свое решение проблемы, которая мучает всех последние дцать лет. Оно входит в Android Architecture Components и в простонародии имеет название ViewModel от Google. Конечно же сами эти вьюмодельки всего лишь классы, которые можно получить напрямую из активити/фрагмента с помощью статических методов провайда. Т.е. первое отличие вьюмодель от презентера в том, что нет необходимости в лишнем интерфейсе вью. Плюс, так как они лежат в статической области — они будут жить своей жизнью, вне зависимости от того, что происходит с нашим активити/фрагмент. Как же тогда вьюмодель узнает обо всех этих изменениях состояния? Для этого наши гении придумали такую интересную вещь как lifeCycleObserver, которая работает в купе с офигенной вещью как LiveData. Живые данные. Вполне себе интересное название, так как живые они просто потому что, мало того, что они не умирают, но еще и умеют реагировать на все события жизненного цикла компонетов андроид. Т.е. тогда, когда активити перейдет в состояние видимости — все события из ливдаты, которые были готовы, но тогда наш экран не был в видимом состоянии (onResume) — будут благополучно доставлены пользователю. Т.е. вот таким вот простым способом мы обходим ручное разруливание всех событий компонентов.

Еще одной гениальной особенностью ливдаты является то, что отдавать данные ей можно из любого потока. Т.е. никаких больше runOnUiThread, Handler и т.д. Стоит добавить еще сюда замечательную интеграцию с котлин и в частности с корутинами. Если вы пишете без корутин — у вас свой велосипед (или тот же хайповый рх), то вам нужно заботиться о таких вещах как очистка потоков, отмена запущенных асинхронных работ (точнее отписка от результата). И под это дело в вьюомоделях от Гугл есть метод onCleared. Только вот если у вас котлин корутины, то вы во-первых получаете скоупвьюмодели, который автоматом очистится в этом методе, плюс вам не нужно писать руками скоупы своих корутин.

Немного личного мнения и истории. Ранее я писал как и многие — MVP/Moxy, Rx, Dagger, Java. И очень скептически относился ко всем нововведениям. Как никак боязнь всего нового, плюс зачем учить/переучиваться, когда у тебя устоявшийся стек. Но как показывает практика — сменив свой стек полностью я ни о чем не жалею. Сейчас я пишукод, используя все блага от Гугла — MVVM/LiveData, Kotlin-coroutines. Добавим сюда навигацию из архитектурных компонентов и воркманагер — вуаля. Больше никаих сервисов, ошибок из-за того, что вы пытаетесь поменять фрагмент в неудобном для системы месте и т.д.

Да, забыл сказать о датабиндинге. В андроид он тоже есть. Это когда вы в хмл файле своего фрагмента/активити пишете код… Выглядит это непривычно и многие не пользуются этим. Наверно дело привычки. Но в любом случае — главный пойнт мввм у нас тоже в андроид есть — связывание данных. Насколько все прекрасно работает я не знаю. Нужно пробовать.

А пока, чтобы не быть голословным — вот вам ссылка на проект, в котором все сделано именно так, как упомянул выше.

Если у вас есть вопросы или замечания или предложения или просто хотите обсудить это все дело со мной — прошу в телеграм — @JohnnySC

Запись опубликована в рубрике Программирование Android. Добавьте в закладки постоянную ссылку.