Шаблон дизайну сховища в Swift

Чистий спосіб запиту ваших моделей

Яку проблему вона вирішує?

Якщо вам потрібно повторно запитувати об’єкти моделі з різних місць у вашому коді, сховище може бути дуже корисним, щоб надати точку входу для роботи з вашими моделями та видалити повторюваний код запиту. Ви можете взяти його ще більше і використовувати його з протоколами, таким чином ви можете легко вимкнути реалізацію (наприклад, для тестових одиниць) або ж використовувати її з дженериками, щоб зробити більш * барабанний ролик * загальну абстракцію. У цій статті я висвітлю всі ці випадки.

Ескіз сцени.

Скажімо, у вас є якийсь код, який отримує дані з API і відображає їх для моделювання об'єктів. У цьому прикладі я отримаю список статей із сервера.

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

Цей фрагмент коду є

  1. GET-запит на сервер
  2. Карти повернутого JSON в масив об’єктів Article
  3. Закриття викликається, коли виконується вся робота.

Для чого нам потрібне сховище?

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

Гаразд ... але коли об'єктом сховища зручно користуватися?
Скажімо, ваша кодова база починає рости, і вам потрібно писати код, щоб отримувати статті знову і знову. Ви можете сказати "скопіюйте код і вставте його куди завгодно, щоб отримати всі статті".

Жодної шкоди не було, ніхто не загинув. Правильно?

У цей момент у вашому мозку повинна почати блимати велика червона тривога.

Привіт сховище.

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

Створимо об’єкт сховища, який надає загальнодоступний API для отримання статей.

Тепер ми можемо називати цей метод, і нам не потрібно турбуватися про те, що відбувається за лаштунками, щоб отримати фактичні статті.
Просто зателефонуйте методу, і ви отримаєте статті. Приємно, правда?
Але зачекай, є ще!

Обробляйте всі взаємодії статей

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

Це робить приємний API для використання у всьому коді, не повторюючи один і той же код знову і знову.

На практиці використання сховища виглядало б так.

Досить приємно і читабельно, правда? Але зачекайте, що це стане ще краще.

Живлення: протоколи

У попередньому коді я завжди використовував приклад "отримання даних з API". Але що робити, якщо вам потрібно додати підтримку для завантаження даних з локального файлу JSON замість інтернет-джерела.

Добре, якщо ви створите протокол, в якому перераховані назви методів, ви можете створити реалізацію для онлайн-API і такий, щоб отримати дані в автономному режимі.

Це могло виглядати приблизно так.

Протокол просто говорить: "Якщо ви відповідаєте мені, вам потрібно мати ці підписи методів, але мене не хвилює реальна реалізація!"

Тож це чудово, ви можете створити сховище WebArticleRepository та LocalArticleRepository. Вони обидва мають усі методи, перелічені в протоколі, але ви можете написати 2 абсолютно різних реалізації.

Живлення: Тестування блоку

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

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

Приклад

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

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

На щастя, це насправді дуже просто вирішити.

Привіт, ін'єкція залежності.

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

Але, можливо, ви замислюєтесь, ну як щодо типів? WebArticleRepository - це не MockArticleRepository, тому компілятор не скаржиться? Ну, не, якщо ви використовуєте протокол як тип. Таким чином, ми повідомляємо компілятору, дозволяємо все, якщо це відповідає протоколу ArticleRepository (що і Web, і MockArticleRepository).

Кінцевий код буде виглядати приблизно так.

І у своєму тесті на одиницю ви можете замінити його так.

Тепер ви маєте повний контроль над тим, які дані повертає ваш сховище.

Супер живлення: дженерики

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

  1. дістати всі речі
  2. дістати деякі речі
  3. вставити деякі речі
  4. видалити річ
  5. оновити річ

Ну єдине, що відрізняється, це слово "річ", тож це може бути відмінним кандидатом для використання протоколу з дженериками. Це може здатися складним, але насправді це зробити досить просто.

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

Тож тепер ми можемо використовувати цей протокол для будь-якого об’єкта моделі.

1. Сховище статей

Компілятор виводить тип T у Article, оскільки, реалізуючи методи, ми вказали, що таке T. У цьому випадку об'єктом статті.

2. Репозиторій користувачів

Це воно.

Сподіваюся, вам сподобалася стаття, і якщо у вас є якісь запитання чи зауваження, просто задайте їх нижче або зв’яжіться зі мною у Twitter і давайте поговоримо.