Repository Design Pattern i Swift

En ren måte å spørre modellene på

Hvilket problem løser det?

Hvis du må spørre om modellobjekter fra forskjellige steder i koden din om og om igjen, kan et depot være veldig nyttig for å gi et enkelt oppføringspunkt for å jobbe med modellene dine og fjerne dupliserte spørringskoder. Du kan ta den enda lenger og bruke den med protokoller, på denne måten kan du enkelt skifte ut implementeringer (for eksempel for enhetstester), eller du kan bruke den med generiske for å lage en mer * trommelvals * generisk abstraksjon. I denne artikkelen vil jeg dekke alle disse sakene.

Skisser scenen.

La oss si at du har en kode som henter data fra et API og kartlegger dette for å modellere objekter. I dette eksemplet henter jeg en liste over artikler fra en server.

Dette kan se litt funky ut, men det er bare RxSwift, og bruker Moya som nettverkets abstraksjonslag, men det betyr ikke noe å forstå hva som skjer. Måten du henter dataene dine er helt opp til deg.

Dette koden gjør det

  1. En FÅ-forespørsel til serveren
  2. Kartlegger den returnerte JSON til en rekke artikkelobjekter
  3. Stengingen blir tilkalt når alt arbeidet er gjort.

Hvorfor trenger vi et depot?

Vel, for øyeblikket gjør vi det ikke. Hvis du bare kaller API-en en gang i hele kodebasen, kan det være for mye å legge til et depot (eller som noen kan si over-engineering).

Ok… men når er et depotobjekt praktisk å bruke?
La oss si at kodebasen din begynner å vokse, og du må skrive koden for å hente artiklene om og om igjen. Du kan si "la oss kopiere koden og lime den inn der du trenger å hente alle artiklene."

Ingen skader, ingen døde. Ikke sant?

I det øyeblikket skulle en stor rød alarm begynne å blinke i hjernen din.

Hei depot.

Et depot er bare et objekt som innkapsler all koden for å spørre modellene dine på ett sted, så du har et enkelt oppføringspunkt hvis du vil ha f.eks. få alle artiklene.

La oss lage et depotobjekt som gir et offentlig API for å få artiklene.

Nå kan vi kalle denne metoden, og vi trenger ikke å bekymre deg for hva som skjer bak kulissene for å få de faktiske artiklene.
Bare ring metoden, så får du artiklene. Fint, ikke sant?
Men vent, det er mer!

Håndter alle artikkelinteraksjoner

Vi kan bruke depotet til å legge til flere metoder for å samhandle med modellobjektet vårt. De fleste ganger vil du utføre CRUD (opprette, lese, oppdatere, slette) operasjoner på modellen. Vel, bare legg til logikken for disse operasjonene i depotet.

Dette gjør en fin API å bruke i hele koden din, uten å måtte gjenta den samme koden igjen og igjen.

I praksis vil bruken av et depot se slik ut.

Ganske fin og lesbar, ikke sant? Men vent, det blir enda bedre.

Oppstart: protokoller

I den forrige koden brukte jeg alltid eksemplet med å "hente data fra en API". Men hva hvis du trenger å legge til støtte for å laste inn data fra en lokal JSON-fil i stedet for en online kilde.

Vel, hvis du oppretter en protokoll som viser metodenavnene, kan du opprette en implementering for online API og en for å få dataene frakoblet.

Dette kan se slik ut.

En protokoll sier bare "hvis du samsvarer med meg, må du ha disse metodene signaturene, men jeg bryr meg ikke om den faktiske implementeringen!"

Så det er flott, du kan opprette et WebArticleRepository og et LocalArticleRepository. De har begge alle metodene som er oppført i protokollen, men du kan skrive to helt forskjellige implementeringer.

Oppstart: Testing av enheten

Bruken av protokoller er også veldig praktisk når du vil enhetstest koden din, fordi du bare kan opprette et annet objekt som implementerer depotprotokollen, men i stedet returnerer spottdata.

Hvis du bruker dette sammen med avhengighetsinjeksjon, gjør det det veldig enkelt å teste et bestemt objekt.

Et eksempel

La oss si at du har en visningsmodell, og visningsmodellen får dataene sine via et depot.

Hvis du vil teste visningsmodellen, sitter du fast med artiklene som blir hentet fra nettet.
Dette er faktisk ikke det vi ønsker. Vi vil at testen vår skal være deterministisk så mye som mulig. I dette tilfellet kan artiklene hentet fra nettet endre seg over tid, det kan ikke være noen internettforbindelse på det tidspunktet testene kjøres, serveren kan være nede, ... dette er alle mulige scenarier der testene våre ville mislyktes, fordi de er utenfor vår kontroll. Og når vi tester, ønsker / trenger vi å ha kontroll.

Heldigvis er det faktisk veldig enkelt å løse dette.

Hallo, avhengighetsinjeksjon.

Du trenger bare å angi artikkelenRepo-egenskapen via initialisatoren. Standardsaken, vil være den du vil ha for produksjonskoden din, og når du skriver en enhetstest, kan du bytte ut depotet med din mock-versjon.

Men kanskje du tenker, og hva med typene? Et WebArticleRepository er ikke et MockArticleRepository, så vil ikke kompilatoren klage? Vel, ikke hvis du bruker protokollen som en type. På denne måten lar vi kompilatoren vite, tillate alt så lenge det samsvarer med ArticleRepository-protokollen (som både nettet og MockArticleRepository gjør).

Den endelige koden vil se slik ut.

Og i enhetstesten din kan du bytte den ut slik.

Nå har du full kontroll over hvilke data depotet ditt returnerer.

Super power-up: generikk

Du kan ta dette enda lenger, ved å bruke generikk. Hvis du tenker på det, har de fleste depoter alltid de samme operasjonene

  1. få alle tingene
  2. få noen av tingene
  3. sett inn noen ting
  4. slett ting
  5. oppdater en ting

Vel, det eneste som er annerledes er ordet ‘ting’, så dette kan være en utmerket kandidat til å bruke en protokoll med generika. Det høres kanskje komplisert ut, men faktisk er det ganske enkelt å gjøre.

Først vil vi gi nytt navn til protokollen til Repository, for å gjøre den mer ... generisk .
Og så fjerner vi alle artikkeltypene og erstatter dem med den magiske T. Men bokstaven T er bare en erstatning for ... alt vi vil at det skal være. Vi trenger bare å merke T som den tilhørende typen protokoll.

Så nå kan vi bruke denne protokollen for alle modellobjekter vi har.

1. Artikkel depot

Kompilatoren vil utlede typen T til artikkel, fordi vi ved å implementere metodene har spesifisert hva T er. I dette tilfellet et artikkelobjekt.

2. User Repository

Det er det.

Jeg håper du likte artikkelen, og hvis du har spørsmål eller kommentarer, bare spør dem nedenfor eller kontakt meg på Twitter og la oss snakke.