Repository Design Pattern i Swift

En ren måde at forespørge dine modeller på

Hvilket problem løser det?

Hvis du har brug for at forespørge dine modelobjekter fra forskellige placeringer i din kode igen og igen, kan et depot være virkelig nyttigt at give et enkelt indgangspunkt til at arbejde med dine modeller og fjerne duplikatforespørgselskode. Du kan tage det endnu længere og bruge det med protokoller, på denne måde kan du nemt skifte implementeringer (f.eks. Til enhedstest), eller du kan bruge det med generiske til at lave en mere * trommelrulle * generisk abstraktion. I denne artikel vil jeg dække alle disse sager.

Skitserer scenen.

Lad os sige, at du har en kode, der henter data fra en API og kortlægger disse til modelobjekter. I dette eksempel henter jeg en liste med artikler fra en server.

Dette ser måske lidt funky ud, men det er bare RxSwift, der bruger Moya som netværksabstraktionslag, men det betyder ikke rigtig noget at forstå, hvad der sker. Den måde, du henter dine data på, er helt op til dig.

Dette kode stykke gør

  1. En GET-anmodning til serveren
  2. Kortlægger den returnerede JSON til en række artikelobjekter
  3. Lukningen kaldes, når alt arbejde er udført.

Hvorfor har vi brug for et lager?

Nå i øjeblikket gør vi ikke det. Hvis du kun ringer til API'en en gang i hele kodebasen, kan tilføjelse af et lager muligvis være overdreven (eller som nogle måske siger over-engineering).

Ok… men hvornår er et depot-objekt praktisk at bruge?
Lad os sige, at din kodebase begynder at vokse, og du er nødt til at skrive koden for at hente artiklerne igen og igen. Du siger måske "lad os kopiere koden og indsætte den, uanset hvor du har brug for at hente alle artiklerne."

Ingen skade foretaget, ingen døde. Højre?

I det øjeblik skulle en stor rød alarm begynde at blinke i hjernen.

Hej depot.

Et depot er bare et objekt, der indkapsler alle koder til forespørgsel om dine modeller ét sted, så du har et enkelt indgangspunkt, hvis du f.eks. Vil have få alle artiklerne.

Lad os oprette et depotobjekt, der giver et offentligt API til at hente artiklerne.

Nu kan vi kalde denne metode, og vi behøver ikke at bekymre os om, hvad der sker bag kulisserne for at få de faktiske artikler.
Bare ring til metoden, så får du artiklerne. Rart, ikke?
Men vent, der er mere!

Håndter alle artikelinteraktioner

Vi kan bruge depotet til at tilføje flere metoder til at interagere med vores modelobjekt. De fleste gange vil du udføre CRUD (oprette, læse, opdatere, slette) operationer på din model. Nå, bare tilføj logikken for disse operationer i depotet.

Dette gør en dejlig API til brug i hele din kode uden at skulle gentage den samme kode igen og igen.

I praksis ser brugen af ​​et arkiv således ud.

Ganske rart og læsbart, ikke? Men vent, det bliver endnu bedre.

Power-up: protokoller

I den forrige kode brugte jeg altid eksemplet med "at hente data fra en API". Men hvad nu hvis du har brug for at tilføje support for at indlæse data fra en lokal JSON-fil i stedet for en online kilde.

Hvis du opretter en protokol, der viser metodenavne, kan du oprette en implementering til online API og en for at få dataene offline.

Dette kan se sådan ud.

En protokol siger bare 'hvis du er i overensstemmelse med mig, skal du have disse metodesignaturer, men jeg er ligeglad med den faktiske implementering!'

Så det er fantastisk, du kan oprette et WebArticleRepository og et LocalArticleRepository. De har begge alle de metoder, der er anført i protokollen, men du kan skrive 2 helt forskellige implementeringer.

Opstart: Test af enheden

Brugen af ​​protokoller er også virkelig praktisk, når du vil enhedsteste din kode, fordi du bare kan oprette et andet objekt, der implementerer depotprotokollen, men i stedet returnerer spottedata.

Hvis du bruger dette sammen med afhængighedsinjektion, gør det det virkelig let at teste et specifikt objekt.

Et eksempel

Lad os sige, at du har en visningsmodel, og visningsmodellen får sine data via et depot.

Hvis du vil teste visningsmodellen, sidder du fast på de artikler, der hentes fra internettet.
Det er faktisk ikke det, vi ønsker. Vi ønsker, at vores test skal være deterministisk så meget som muligt. I dette tilfælde kunne de hentede artikler fra internettet ændre sig over tid, der kunne ikke være nogen internetforbindelse på det tidspunkt, testene køres, serveren kunne være nede,… dette er alle mulige scenarier, hvor vores test ville mislykkes, fordi de er uden for vores kontrol. Og når vi tester, ønsker / skal vi have kontrol.

Heldigvis er det faktisk virkelig simpelt at løse dette.

Hej, afhængighedsinjektion.

Du skal bare indstille egenskaben artikelRepo via initialisatoren. Standardtilstanden er den, du vil have til din produktionskode, og når du skriver en enhedstest, kan du bytte depotet med din mock-version.

Men måske tænker du, hvad med typerne? Et WebArticleRepository er ikke et MockArticleRepository, så vil kompilatoren ikke klage? Nå, ikke hvis du bruger protokollen som en type. På denne måde lader vi kompilatoren vide, tillade alt, så længe det er i overensstemmelse med ArticleRepository-protokollen (som både web og MockArticleRepository gør).

Den endelige kode ser sådan ud.

Og i din enhedstest kan du udveksle den sådan.

Nu har du fuld kontrol over hvilke data dit lager returnerer.

Super power-up: generik

Du kan tage dette endnu længere ved at bruge generik. Hvis du tænker over det, har de fleste depoter altid de samme operationer

  1. få alle tingene
  2. få nogle af tingene
  3. indsæt nogle ting
  4. slet ting
  5. opdater en ting

Det eneste, der er anderledes, er ordet 'ting', så dette kan være en fremragende kandidat til at bruge en protokol med generiske. Det lyder måske kompliceret, men faktisk er det ganske enkelt at gøre.

Først omdøber vi protokollen til Repository for at gøre den mere ... generisk .
Og så fjerner vi alle artikeltyperne og erstatter dem med den magiske T. Men bogstavet T er bare en erstatning for ... alt det, vi vil have, at det skal være. Vi skal bare markere T som den tilhørende type protokol.

Så nu kan vi bruge denne protokol til ethvert modelobjekt, vi har.

1. Artikelopbevaring

Compileren udleder typen T til artikel, fordi vi ved at implementere metoderne har specificeret, hvad T er. I dette tilfælde er en artikel genstand.

2. Brugerlager

Det er det.

Jeg håber, at du nød artiklen, og hvis du har spørgsmål eller bemærkninger, skal du bare spørge dem nedenfor eller række ud til mig på Twitter og lad os snakke med en chat.