19 мая 2017

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

Каждый программист под платформу Android начинает задаваться вопросами:

  • Как разбить сложную логику в Activity и Fragments?
  • Использовать ли в новых проектах модные паттерны и подходы программирования?
  • Чем отличается удачная архитектура от неудачной?
  • Почему используется MVP для Android?
  • Все говорят о Moxy, интересно, но применять или нет?

Данная статья нашего Android-разработчика Татьяны может помочь Android разработчикам, особенно начинающим, составить представление об основных возможностях MVP, Moxy и Routers в разработке под платформу Android.

MVP – “must know” паттерн

Как выглядит прямолинейная разработка под Android? Activity и Fragments выполняют функции как View, так и Controller и содержат: код для отображения и контроля элементов UI, код для обработки событий UI, запуск асинхронных задач для получения и обработки данных, запуск дополнительных компонентов приложения и обработку их результатов. И все эти операции жестко связаны между собой, что не позволяет провести раздельное тестирование. Разбираться в работе Activity можно пару часов (если, например, это долгоживущий проект, которому нужны дополнения). Так как методы отображения результата и его обработки/получения смешаны, читать функции становится сложнее. Часто допускаются ошибки «показать неверное состояние экрана» / «неправильно вычислить данные для UI» / «выполнить неверную навигацию» и прочие, потому что разработка начинает сводиться к навыкам внимательности и аккуратности, а не умения мыслить.

Широко известная концепция MVP заключается в разделении ответственности в коде: бизнес-логика выносится в Presenter, оставляя Activity и Fragments работу View. Presenter выступает в роли посредника между данными Model и отображением View: содержит асинхронные и прямые обращения к Model и вызов методов View для отображения подготовленных данных. События UI-я делегируются Presenter-у, который решает, как реагировать на событие, и контролирует поведение компонентов UI.

MVP позволяет дробить сущности одновременно «мелко» и «удобно», код становится более семантичным, классы имеют ясную, просматриваемую логику, классы отвечают принципу Single Responsibility. Лучше при реализации использовать Dagger, что позволит упростить переиспользование классов: достаточно подключить соответствующий класс и позаимствовать узкий функционал, без лишних усложнений.

При подключении нового разработчика к проекту его адаптация к коду значительно ускоряется. Можно начать работу в рамках всего нескольких классов, постепенно включаясь в общую концепцию приложения. Казалось бы — обычное дело! Но именно с паттерном MVP просмотреть все необходимые классы «с ходу» не составит труда.

Основной минус, о котором часто можно услышать от разработчиков: слишком много кода. Чаще всего такое впечатление может сложиться, когда возможности MVP не использованы в приложении, тогда действительно простые команды UI начинают выглядеть запутанно. Но не стоит ли в этом случае задуматься: нужен ли вообще MVP в проекте, где этот паттерн не требуется, где глубокого функционала нет и все события UI выливаются в односложные вызовы других событий UI?

Минус лишнего кода полностью покрывается плюсами, ведь как просто вносить изменения в существующие классы Model-View-Presenter. Изменения полностью изолированы, обычно количество функций в каждом классе в среднем 3-6, при чистом написании кода (без лишнего дублирования) найти место для исправления очень просто. Кроме того, boilerplate-код приходится писать лишь в начале разработки, когда архитектура приложения только закладывается.

Не стоит забывать о том, что проект чаще всего разрабатывается совместно, при использовании MVP возникновение конфликтов при работе с GIT значительно уменьшается.

О выборе архитектуры приложения

Многие знают, как выглядят проекты некоторых компаний, предпочитающих каждую фичу в приложении отдавать на outsourcing. При виде такого проекта в голову приходит лишь одна мысль (кроме желания все сжечь): “Проще не разбираться в коде, а написать приложение с нуля”. Программисты, вынужденные работать с такими проектами, по-настоящему едва выживают со своим кодом. Такие проекты могут быть отличным уроком, как НЕ надо делать. В этом и заключается ключевой признак плохой архитектуры: ее сложно поддерживать – при добавлении нового функционала нужно починить/переписать некоторый существующий функционал. Чем больше такое приложение, тем больше в нем костыльных решений с подозрительными комментариями в коде.

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

Одной из удачных и понятных архитектур, развивающих подход MVP глубже, можно назвать 3-Tier Architecture — трехслойная модель Presentation-Domain-Data. Архитектурный подход заключается в дополнительном абстрактном разделении ответственности классов. На уровне Presentation содержатся классы, взаимодействующие и контролирующие UI (классы View, Presenter, классы-навигаторы для роутеров, дополнительные вспомогательные классы для работы с элементами UI-я). На уровне Domain находится бизнес-логика приложения, например, классы-менеджеры, обрабатывающие и преобразующие данные; классы-обработчики ввода/вывода; классы, отвечающие за дополнительную логику (сложные вычислительные операции, утилитные классы). На уровне Data находятся сервисы, предоставляющие данные, клиенты серверов и баз данных, организация баз данных, POJO классы бизнес-модели.

При выборе архитектурного подхода для приложения нужно руководствоваться принципами SOLID. Нет общепринятых стандартов, приступая к разработке, нужно думать о том, какие требования к приложению на данный момент и куда могут развиваться эти требования в дальнейшем. Таким образом, архитектура выбирается под конкретные требования проекта и важно не усложнить простое приложение сложной архитектурой, необходимости в которой нет.

Об использовании Moxy+MVP

http://security-corp.org/programming/vebdev/31500-moxy-realizaciya-mvp-pod-android.html

Moxy дополняет и развивает MVP тем, что между View и Presenter находится важная сущность ViewState, в которой находится очередь команд к View. Presenter передает команды View не напрямую, а посредством ViewState. ViewState гарантирует, что когда View будет в готовом состоянии для выполнения команд Presenter-а, тогда команды и будут последовательно применены из очереди. Presenter не привязан к View: события жизненного цикла View не приведут к пересозданию Presenter-а. Это открывает новые возможности при использовании паттерна MVP: изменить из одного Presenter-а несколько View, подключить несколько Presenter-ов к одной View. При использовании нескольких Presenter-ов важно не забывать о том, что они не могут взаимодействовать друг с другом. Любое взаимодействие должно быть выполнено через Model.

Moxy предоставляет очень гибкие возможности для создания собственных стратегий, таким образом, очередь команд для View полностью под контролем у разработчика.

Moxy не является подходом, который может быть использован программистом-новичком. Для его использования нужен достаточный опыт не только разработки под платформу Android, но и применения паттерна MVP. Кроме того, должно быть четкое понимание стратегий Moxy и очереди команд для View. Необдуманное использование неверной стратегии может привести к непредсказуемому поведению в приложении.

Интересно, что разработчик-создатель библиотеки Mosby, которая предоставляет схожие возможности для контроля View, не является сторонником декомпозиции Presenter-а, предпочитая стратегию, при которой каждая View привязана к единственному Presenter-у и наоборот. При выборе стратегии по связыванию View и Presenter-ов руководствуйтесь здравым смыслом, ключевой является цель избежать создание God-Presenter для ситуаций, когда на экране объединено большое количество логики. Подключить несколько Presenter-ов или выполнить полную декомпозицию экрана на несколько View с несколькими Presenter-ами — это решение, основанное на конкретных потребностях UI и связанного с ним функционала.

Об использовании Routers в Android

Использование Routers (библиотека Cicerone для Android) при разработке дополнительно облегчает код внутри классов Presenter-ов. Для разработчика, особенно новичка, это означает лишь одно — вся ответственность за маршрутизацию собрана в отдельных классах-роутерах, функциональность которых скрыта, но предоставляется интерфейс для использования. Она вынесена, отлажена и можно простым языком сказать «Router, please, navigate to my awesome class» и быть уверенным, что навигация будет осуществлена без ошибок и своевременно. Для более опытных разработчиков важны такие достоинства как: легкое наследование, можно описать любой переход последовательным вызовом команд внутри роутера, копипасты, связанной с навигацией, просто НЕТ.

Presenter обращается с роутерами так же, как с View: вызывает соответствующие функции роутера в нужных местах. View и Presenter ничего не знают о создании новых окон приложения или о том, какие окна в стеке приложения. Роутеры — это подход, уменьшающий количество кода, они отвечают за работу со стеком приложения, избавляя разработчика от ручного контроля стека.

Взаимодействовать с роутерами должен именно Presenter — ведь это именно он обрабатывает события UI и знает, куда данные должны пойти дальше, куда экран приложения должен развиваться дальше.

Классы навигаторы содержат логику перехода и формирования данных для навигации. У Presenter-а только «есть что отдать», тогда как навигаторы определяют, «как отдать» информацию дальше, какой класс инстанцировать. Presenter частично освобождается от логики контроллера, превращаясь в чистый класс взаимодействия Model-UI, UI-Model. Важное правило: каждый роутер связан лишь с одним навигатором. Тогда Presenter-у достаточно подключить нужный роутер, что способствует переиспользованию Presenter-ов.

Использование роутеров не связано с выбором MVP. Вынесение логики для навигации значительно упростит Activity и Fragments — они будут знать только, как создать себя и обработать свои данные. Подход способствует облегчению связанности компонентов приложения, позволяя при разработке сосредоточиться на функционале и данных конкретного экрана.

Заключение

В статье рассмотрены основные возможности разработки с использованием MVP и Moxy со своими плюсами и минусами.

Паттерн MVP является обязательным к изучению для всех разработчиков под платформу Android, тем более, что он не сложный для понимания. Если хочется общаться с программой на языке высокого уровня, то паттерн напрашивается “сам по себе”.

Обычно при разработке используется MVP вместе с Dagger и RxJava. Для обработки жизненного цикла Activity и Fragments можно использовать Moxy, для навигации – Cicerone. Для уменьшения boilerplate в коде используйте ButterKnife и Lombok plugin.

Проектирование и реализация удачной архитектуры – самый важный и ответственный этап в разработке, поэтому не стоит пренебрегать временем на создание скелета приложения и выбор используемых технологий.

Надеюсь, данный обзор будет полезным при выборе стратегии для вашего будущего проекта.

Ссылки на материалы, которые более подробно раскрывают технические особенности технологий и подходов, рассматриваемых в данной статье:

  1. https://blog.mindorks.com/essential-guide-for-designing-your-android-app-architecture-mvp-part-1-74efaf1cda40
  2. http://security-corp.org/programming/vebdev/31500-moxy-realizaciya-mvp-pod-android.html
  3. https://habrahabr.ru/company/mobileup/blog/314838/
  4. https://en.wikipedia.org/wiki/Multitier_architecture

Если вы нашли ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter.

Читайте в нашем блоге

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: