- PVSM.RU - https://www.pvsm.ru -
От переводчика: Некоторые термины, которые использует автор, не имеют общепринятого перевода (ну, или я его не знаю:), поэтому я решил оставить большинство на языке оригинала — они всё равно понятны и для тех, кто пишет под android, но не знает английский.
Куда писать об ошибках и неточностях, вы знаете.
За последние несколько месяцев, а также после дискуссий на Tuenti [1] с коллегами вроде @pedro_g_s [2] и @flipper83 [3] (кстати говоря, 2 крутых Android-разработчика), я решил, что имеет смысл написать заметку о проектировании Android-приложений.
Цель поста — немного рассказать о подходе к проектированию, который я продвигал в последние несколько месяцев, и также поделиться всем тем, что я узнал во время исследования и реализации этого подхода.
Мы знаем, что написание качественного программного обеспечение — сложное и многогранное занятие: программа должна не только соответствовать установленным требованиям, но и быть надёжной, удобной в сопровождении, тестируемой и достаточно гибкой для добавления или изменения функций. И здесь появляется понятие “стройной архитектуры”, которое неплохо бы держать в уме при разработке любого приложения.
Идея проста: стройная архитектура основывается на группе методов, реализующих системы, которые являются:
Кругов необязательно должно быть 4 (как на диаграмме), это просто схема; важно учитывать Правило Зависимостей: код должен иметь зависимости только во внутренние круги и не должен иметь никакого понятия, что происходит во внешних кругах.
Вот небольшой словарь терминов, необходимых для лучшего понимания данного подхода:
Я начал с простого, чтобы понять, как обстоят дела: создал простенькое приложение, которое отображает список друзей, который получает с облака, а при клике по другу отображает более детальную информацию о нём на новом экране.
Я оставлю здесь видео, чтобы вы поняли, о чем идёт речь:
Наша цель — разделение задач таким образом, чтобы бизнес-логика ничего не знала о внешнем мире, так, чтобы можно было тестировать её без никаких зависимостей и внешних элементов.
Для достижения этого я предлагаю разбить проект на 3 слоя, каждый из которых имеет свою цель и может работать независимо от остальных.
Стоит отметить, что каждый слой использует свою модель данных, таким образом можно достигнуть необходимой независимости (вы можете увидеть в коде, для выполнения трансформации данных небходим преобразователь данных (data mapper). Это вынужденная плата за то, чтобы модели внутри приложения не пересекались). Вот схема, как это выглядит:
Примечание: я не использовал внешних библиотек (кроме gson для парсинга json и junit, mockito, robolectric и espresso для тестирования) для того, чтобы пример был более наглядным. В любом случае, не стесняйтесь использовать ORM для хранения информации или любой dependency injection framework, да и вообще инструменты или библиотеки, которые делают вашу жизнь легче (помните: изобретать велосипед заново — не лучшая идея).
Здесь логика связывается с Views (Представлениями) и происходят анимации. Это не что иное, как Model View Presenter (то есть, MVP [6]), но вы можете использовать любой другой паттерн вроде MVC или MVVM. Я не буду вдаваться в детали, но fragments и activities — это всего лишь views, там нету никакой логики кроме логики UI и рендеринга этого самого отображения. Presenter-ы на этом слое связываются с interactors (посредниками), что предполагает работу в новом потоке (не в UI-потоке), и передачи через коллбэки информации, которая будет отображена во view.
Если вы хотите увидеть крутой пример эффективного Android UI [7], который использует MVP и MVVM, взгляните на пример реализации от моего друга Pedro Gómez.
Вся логика реализована в этом слое. Рассматривая проект, вы увидите здесь реализацию interactor-ов (Use Cases — методы использования).
Этот слой — модуль на чистой джаве без никаких Android-зависимостей. Все внешние компоненты используют интерфейсы для связи с бизнес-объектами.
Все данные, необходимые для приложения, поставляются из этого слоя через реализацию UserRepository (интерфейс находится в domain layer — слое бизнес-логики), который использует Repository Pattern [8] со стратегией, которая, через фабрику, выбирает различные источники данных, в зависимости от определенных условий.
Например, для получения конкретного пользователя по id источником данных выбирается дисковый кэш, если пользователь уже загружен в него, в противном случае облаку отправляется запрос на получение данных для дальнейшего сохранения в тот же кэш.
Идея всего этого заключается в том, что происхождение данных является понятным для клиента, которого не волнует, поступают данные из памяти, кэша или облака, ему важно только то, что данные будут получены и доступны.
Примечание: что касается кода, помня, что код — это учебный пример, я реализовал очень простой, даже примитивный кэш, используя хранения shared preferences. Помните: НЕ СТОИТ ИЗОБРЕТАТЬ ВЕЛОСИПЕД, если существуют библиотеки, хорошо решающие поставленную задачу.
Это большая тема, в которой всегда есть, что обсудить (и здесь автор предлагает делиться своими решениями). Что до моей реализации, я использовал коллбэки, которые, на случай, если что-то случается, скажем, в хранилище данных, имеют 2 метода: onResponse() и onError(). Последний инкапсулирует исключения в wrapper class (класс-обертку) под названием “ErrorBundle”: Такой подход сопряжен с некоторыми трудностями, потому что бывают цепочки обратных вызовов один за другим, пока ошибка не выходит на presentation layer, чтобы отобразиться. Читаемость кода из-за этого может быть немного нарушена.
С другой стороны, я реализовал систему event bus, которая бросает события, если что-то не так, но такое решение похоже на использование GOTO [9], и, по-моему, иногда, если не управлять событиями предельно внимательно, можно заблудиться в цепи событий, особенно когда их одновременно бросается несколько.
Что касается тестирования, я применил несколько решений, в зависимости от слоя.
Думаю, в этом месте вам интересно посмотреть на код? Что ж, вот ссылка на github [10], где можно найти, что же у меня получилось. Что стоит упомянуть о структуре папок так это то, что разные слои представлены разными модулями:
Дядя Боб сказал: “Архитекрута — это намерения, а не фреймворки”, и я полностью с ним согласен. Конечно, есть разные способы реализовать какую-либо вещь, и я уверен, что вы, как и я, каждый день сталкиваетесь с трудностями в этой области, но используя данные приёмы, вы сможете быть уверены, что ваше приложение будет:
В заключение я настоятельно рекомендую вам попробовать эти методы, посмотреть на результат и поделиться своим опытом как в отношении этого, так любого другого подхода, который, по вашим наблюдениям, работает лучше: мы уверены, что постоянное совершенствование всегда полезно и положительно.
Автор: Bringoff
Источник [13]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/android-development/83038
Ссылки в тексте:
[1] Tuenti: http://corporate.tuenti.com/en/dev/blog
[2] @pedro_g_s: https://twitter.com/pedro_g_s
[3] @flipper83: https://twitter.com/flipper83
[4] данной статье: http://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html
[5] данному видео: http://vimeo.com/43612849
[6] MVP: http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93presenter
[7] эффективного Android UI: https://github.com/pedrovgs/EffectiveAndroidUI/
[8] Repository Pattern: http://martinfowler.com/eaaCatalog/repository.html
[9] GOTO: http://www.drdobbs.com/jvm/programming-with-reason-why-is-goto-bad/228200966
[10] вот ссылка на github: https://github.com/android10/Android-CleanArchitecture
[11] Архитекрута — это намерения, а не фреймворки: http://www.infoq.com/news/2013/07/architecture_intent_frameworks
[12] Презентация по Android Design Patterns: http://www.slideshare.net/PedroVicenteGmezSnch/
[13] Источник: http://habrahabr.ru/post/250659/
Нажмите здесь для печати.