Модел на дизайн на хранилището в 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. актуализирайте нещо

Е, единственото, което е различно, е думата "нещо", така че това може да е отличен кандидат да използва протокол с генерични данни. Може да звучи сложно, но всъщност е доста просто да се направи.

Първо ще преименуваме протокола в хранилище, за да го направим повече ... общ.
И тогава ще премахнем всички видове статии и ще ги заменим с магията Т. Но буквата Т е просто заместител на ... всичко, което искаме да бъде. Просто трябва да маркираме Т като асоцииран тип на протокола.

Така че сега можем да използваме този протокол за всеки моделен обект, който имаме.

1. Съхранение на статии

Компилаторът ще изведе типа T на Article, тъй като прилагайки методите, ние уточнихме какво е T. В този случай предмет на член.

2. Потребителско хранилище

Това е.

Надявам се, че ви е харесала статията и ако имате някакви въпроси или забележки, просто ги задайте по-долу или се свържете с мен в Twitter и нека си поговорим.