Архітектура Android-додатків... Правильний шлях?

Від перекладача: Деякі терміни, які використовує автор, не мають загальноприйнятого перекладу (ну, або я його не знаю:), тому я вирішив залишити більшість мовою оригіналу — вони однаково зрозумілі і для тих, хто пише під android, але не знає англійську.
Куди писати про помилки і неточності, ви знаєте.


За останні кілька місяців, а також після дискусій на Tuenti з колегами зразок @pedro_g_s і @flipper83 (до речі кажучи, 2 крутих Android-розробника), я вирішив, що має сенс написати замітку про проектуванні Android-додатків.

Мета посту — трохи розповісти про підході до проектування, який я просував в останні кілька місяців, і поділитися всім тим, що я дізнався під час дослідження та реалізації цього підходу.

Приступаючи до роботи
Ми знаємо, що написання якісного програмного забезпечення — складне і багатогранне заняття: програма повинна не тільки відповідати встановленим вимогам, але і бути надійною, зручною у супроводі, тестуємо і достатньо гнучкою для додавання або зміни функцій. І тут з'являється поняття «стрункої архітектури», що непогано було б тримати в голові при розробці будь-якого додатку.

Ідея проста: струнка архітектура ґрунтується на групу методів, що реалізують системи, які є:

  • Незалежними від фреймворків.
  • Тестованими.
  • Незалежними від UI.
  • Незалежними від БД.
  • Незалежними від будь-якої зовнішньої служби.


Кругова діаграма

Кіл необов'язково має бути 4 (як на діаграмі), це просто схема; важливо враховувати Правило Залежностей: код повинен мати залежності тільки у внутрішні кола і не повинен мати ніякого поняття, що відбувається у зовнішніх колах.

Ось невеликий словник термінів, необхідних для кращого розуміння даного підходу:

  • Entities (Сутності): це бізнес-логіка програми.
  • Use Cases (Методи використання): ці методи організовують потік даних у Entities і з них. Також їх називають Interactors (Посередниками).
  • Interface Adapters (Інтерфейс-Адаптери): цей набір адаптерів перетворює дані з формату, зручного для використання Методів і Сутностей. До цих адаптерами належать Presenter-и і Controller-и.
  • Frameworks and Drivers: місце скупчення деталей: UI, інструменти, фреймворки, БД і т. д.
    Для кращого розуміння зверніться до даній статті або цього відео.


Наш сценарій
Я почав з простого, щоб зрозуміти, як йдуть справи: створив простеньке додаток, яке відображає список друзів, який отримує з хмари, а при кліці по одному відображає більш детальну інформацію про нього на новому екрані.
Я залишу тут відео, щоб ви зрозуміли, про що йде мова:



Android-архітектура
Наша мета — поділ завдань таким чином, щоб бізнес-логіка нічого не знала про зовнішньому світі, так, щоб можна було тестувати її без жодних залежностей і зовнішніх елементів.
Для досягнення цього я пропоную розбити проект на 3 шари, кожен з яких має свою мету і може працювати незалежно від інших.

Варто зазначити, що кожен шар використовує свою модель даних, таким чином можна досягти необхідної незалежності (ви можете побачити в коді, для виконання трансформації даних небходим перетворювач даних (data mapper). Це вимушена плата за те, щоб моделі всередині програми не перетиналися). Ось схема, як це виглядає:

3 шари

Примітка: я не використовував зовнішніх бібліотек (крім gson для парсингу json і junit, mockito, robolectric і espresso для тестування) для того, щоб приклад був наочним. У будь-якому випадку, не соромтеся використовувати ORM для зберігання інформації або будь-dependency injection framework, та й взагалі інструменти або бібліотеки, які роблять ваше життя легше (пам'ятаєте: винаходити велосипед заново — не найкраща ідея).

Presentation Layer (Шар подання)
Тут логіка зв'язується з Views (Поданнями) і відбуваються анімації. Це не що інше, як Model View Presenter (тобто, MVP), але ви можете використовувати будь-який інший патерн начебто MVC або MVVM. Я не буду вдаватися в деталі, але fragments і activities — це всього лише views, там немає ніякої логіки крім логіки UI і рендеринга цього самого відображення. Presenter-и на цьому шарі зв'язуються з interactors (посередниками), що передбачає роботу у новому потоці (не в UI-потоці), і передачі через коллбэки інформації, яка буде відображена у view.

image

Якщо ви хочете побачити крутий приклад ефективного Android UI, який використовує MVP і MVVM, погляньте на приклад реалізації від мого друга Pedro Gómez.

Domain Layer (Шар бізнес-логіки)
Вся логіка реалізована в цьому шарі. Розглядаючи проект, ви побачите тут реалізацію interactor-ів (Use Cases — методи використання).

Цей шар — модуль на чистій джаві без жодних Android-залежностей. Всі зовнішні компоненти використовують інтерфейси для зв'язку з бізнес-об'єктами.

domain layer

Data Layer (Шар даних)
Всі дані, необхідні для програми, що поставляються з цього шару через реалізацію UserRepository (інтерфейс знаходиться в domain layer — шарі бізнес-логіки), який використовує Pattern Repository зі стратегією, яка, через фабрику, вибирає різні джерела даних, в залежності від певних умов.

Наприклад, для отримання конкретного користувача з id джерелом даних вибирається дисковий кеш, якщо користувач вже завантажений в нього, в іншому випадку хмари відправляється запит на отримання даних для подальшого збереження в той же кеш.

Ідея усього цього полягає в тому, що походження даних є зрозумілим для клієнта, якого не хвилює, надходять дані з пам'яті, кеш або хмари, йому важливо лише те, що дані будуть отримані і доступні.

repository implementation

Примітка: що стосується коду, пам'ятаючи, що код — це навчальний приклад, я реалізував дуже простий, навіть примітивний кеш, використовуючи зберігання shared preferences. Пам'ятайте: НЕ ВАРТО ВИНАХОДИТИ ВЕЛОСИПЕД, якщо існують бібліотеки, добре вирішують поставлену задачу.

Обробка помилок
Це велика тема, в якій завжди є, що обговорити (і тут автор пропонує ділитися своїми рішеннями). Що до моєї реалізації, я використовував коллбэки, які, на випадок, якщо щось трапляється, скажімо, в сховище даних, мають 2 методу: onResponse() і onError(). Останній інкапсулює виключення в wrapper class (клас-обгортку) під назвою «ErrorBundle»: Такий підхід пов'язаний з деякими труднощами, тому що бувають ланцюжка зворотних викликів один за іншим, поки помилка не виходить на presentation layer, щоб з'явитися. Читаність коду з-за цього може бути трохи порушена.

З іншого боку, я реалізував систему event bus, яка кидає події, якщо щось не так, але таке рішення схоже на використання GOTO, і, по-моєму, іноді, якщо не керувати подіями гранично уважно, можна заблукати в ланцюзі подій, особливо коли їх одночасно впадає кілька.

Тестування
Що стосується тестування, я застосував кілька рішень, в залежності від шару.

  • Presentation Layer: існуючий android інструментал і espresso для інтеграції і функціонального тестування.
  • Domain Layer: JUnit + mockito використовувалися для юніт-тестів.
  • Data Layer: Robolectric (так як цей шар має android-залежності) + junit + інтеграції та юніт-тестів.


Покажіть мені код
Думаю, в цьому місці вам цікаво подивитися на код? Що ж, ось посилання на github, де можна знайти, що ж у мене вийшло. Що варто згадати про структуру папок, так це те, що різні шари представлені різними модулями:

  • presentation: це android-модуль для presentation layer.
  • domain: java-модуль без android-залежностей.
  • data: android-модуль, звідки надходять дані.
  • data-test: тести для data layer. З-за певних обмежень при використанні Robolectric мені довелося використовувати окремий java-модуль.


Висновок
Дядько Боб сказав: «Архитекрута — це наміри, а не фреймворки», і я повністю з ним згоден. Звичайно, є різні способи реалізувати яку-небудь річ, і я впевнений, що ви, як і я, щодня стикаєтеся з труднощами в цій області, але використовуючи дані прийоми, ви зможете бути впевнені, що ваш додаток буде:

  • Простим підтримки.
  • Простим у тестуванні.
  • Становити єдине ціле,
  • Будучи розділеним.


На закінчення я настійно рекомендую вам спробувати ці методи, подивитися на результат і поділитися своїм досвідом як щодо цього, так будь-якого іншого підходу, який, за вашими спостереженнями, працює краще: ми впевнені, що постійне вдосконалення завжди корисно і позитивно.

Посилання і джерела
  1. Код: https://github.com/android10/Android-CleanArchitecture
  2. The clean architecture від Дядька Боба
  3. Архитекрута — це наміри, а не фреймворки
  4. Model View Presenter
  5. Pattern Repository від Martin Fowler
  6. Презентація з Android Design Patterns

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.