Repository-ontwerppatroon in Swift

Een schone manier om uw modellen te bevragen

Welk probleem lost het op?

Als u uw modelobjecten steeds opnieuw vanuit verschillende locaties in uw code moet opvragen, kan een repository erg handig zijn om een ​​toegangspunt voor uw modellen te bieden en dubbele querycode te verwijderen. Je kunt nog verder gaan en het gebruiken met protocollen, op deze manier kun je eenvoudig implementaties uitschakelen (bijvoorbeeld voor unit-tests) of je kunt het gebruiken met generieke geneesmiddelen om een ​​meer * drumrol * generieke abstractie te maken. In dit artikel zal ik al deze gevallen behandelen.

Schets de scène.

Stel dat u code hebt die gegevens van een API ophaalt en deze toewijst aan modelobjecten. In dit voorbeeld haal ik een lijst met artikelen op van een server.

Dit lijkt misschien een beetje funky, maar het is gewoon RxSwift, met Moya als de netwerkabstractielaag, maar dat maakt niet echt uit om te begrijpen wat er gebeurt. De manier waarop u uw gegevens ophaalt, is helemaal aan u.

Dit stukje code doet dat

  1. Een GET-verzoek aan de server
  2. Wijst de geretourneerde JSON toe aan een reeks artikelobjecten
  3. De sluiting wordt opgeroepen als al het werk is gedaan.

Waarom hebben we een repository nodig?

Nou op dit moment doen we dat niet. Als u de API slechts eenmaal in uw volledige codebase aanroept, kan het toevoegen van een gegevensopslagruimte te veel zijn (of zoals sommigen zeggen over-engineering).

Ok ... maar wanneer is een repository-object handig om te gebruiken?
Stel dat uw codebase begint te groeien en dat u de code moet schrijven om de artikelen steeds opnieuw op te halen. Je zou kunnen zeggen: "laten we de code kopiëren en plakken waar je maar wilt om alle artikelen op te halen."

Geen kwaad gedaan, niemand stierf. Rechts?

Op dat moment begint er een groot rood alarm in je hersenen te knipperen.

Hallo repository.

Een repository is gewoon een object dat alle code inkapselt om op één plek naar uw modellen te zoeken, dus u hebt één invoerpunt als u wilt, b.v. krijg alle artikelen.

Laten we een repository-object maken dat een openbare API biedt om de artikelen te krijgen.

Nu kunnen we deze methode noemen en hoeven we ons geen zorgen te maken over wat er achter de schermen gebeurt om de werkelijke artikelen te krijgen.
Roep gewoon de methode aan en je krijgt de artikelen. Mooi toch?
Maar wacht, er is meer!

Verwerk alle artikelinteracties

We kunnen de repository gebruiken om meer methoden toe te voegen voor interactie met ons modelobject. Meestal wilt u CRUD-bewerkingen (maken, lezen, bijwerken, verwijderen) op uw model uitvoeren. Welnu, voeg gewoon de logica voor deze bewerkingen toe in de repository.

Dit maakt een mooie API om in uw code te gebruiken, zonder dezelfde code steeds opnieuw te moeten herhalen.

In de praktijk zou het gebruik van een repository er zo uitzien.

Heel mooi en leesbaar, toch? Maar wacht, het wordt nog beter.

Power-up: protocollen

In de vorige code heb ik altijd het voorbeeld gebruikt van 'gegevens ophalen uit een API'. Maar wat als u ondersteuning moet toevoegen om gegevens van een lokaal JSON-bestand te laden in plaats van een online bron.

Als u een protocol maakt met de namen van de methoden, kunt u een implementatie voor de online API maken en een implementatie om de gegevens offline te krijgen.

Dit kan er zo uitzien.

Een protocol zegt alleen 'als je je aan mij conformeert, moet je deze methoden hebben, maar ik geef niets om de daadwerkelijke implementatie!'

Dus dat is geweldig, je kunt een WebArticleRepository en een LocalArticleRepository maken. Ze hebben allebei alle methoden die in het protocol worden vermeld, maar u kunt 2 totaal verschillende implementaties schrijven.

Opstarten: testen van eenheden

Het gebruik van protocollen is ook erg handig als u uw code in eenheden wilt testen, omdat u gewoon een ander object kunt maken dat het repository-protocol implementeert, maar in plaats daarvan nepgegevens retourneert.

Als u dit samen met afhankelijkheidsinjectie gebruikt, is het heel eenvoudig om een ​​specifiek object te testen.

Een voorbeeld

Stel dat u een weergavemodel hebt en dat het weergavemodel de gegevens via een gegevensopslagruimte ophaalt.

Als u het weergavemodel wilt testen, zit u vast aan de artikelen die van internet worden opgehaald.
Dit is eigenlijk niet wat we willen. We willen dat onze test zoveel mogelijk deterministisch is. In dit geval kunnen de artikelen die van internet zijn opgehaald in de loop van de tijd veranderen, er kan geen internetverbinding zijn op het moment dat de tests worden uitgevoerd, de server kan niet werken, ... dit zijn allemaal mogelijke scenario's waarin onze tests zouden mislukken, omdat ze zijn buiten onze controle. En wanneer we testen, willen / moeten we de controle hebben.

Gelukkig is het eigenlijk heel eenvoudig om dit op te lossen.

Hallo, afhankelijkheid injectie.

U hoeft alleen de eigenschap ArticleRepo in te stellen via de initialisatie. Het standaardgeval is het geval dat u wilt voor uw productiecode en wanneer u een eenheids-test schrijft, kunt u de repository met uw proefversie verwisselen.

Maar misschien denk je, en hoe zit het met de typen? Een WebArticleRepository is geen MockArticleRepository, dus zal de compiler niet klagen? Nou, niet als je het protocol als een type gebruikt. Op deze manier laten we de compiler weten, laten alles toe zolang het voldoet aan het ArticleRepository-protocol (wat zowel het Web als MockArticleRepository doen).

De uiteindelijke code zou er zo uitzien.

En in je unit-test kun je het zo uitwisselen.

Nu heb je volledige controle over welke gegevens je repository retourneert.

Super power-up: generiek

Je zou dit nog verder kunnen verbeteren door generieke geneesmiddelen te gebruiken. Als je erover nadenkt, hebben de meeste opslagplaatsen altijd dezelfde bewerkingen

  1. krijg alle dingen
  2. haal een paar dingen
  3. enkele dingen invoegen
  4. ding verwijderen
  5. iets bijwerken

Wel, het enige dat anders is, is het woord ‘ding’, dus dit kan een uitstekende kandidaat zijn om een ​​protocol met generieke geneesmiddelen te gebruiken. Het klinkt misschien ingewikkeld, maar het is eigenlijk vrij eenvoudig om te doen.

Eerst zullen we het protocol hernoemen naar Repository, om het meer ... generiek te maken.
En dan zullen we alle artikeltypen verwijderen en vervangen door de magische T. Maar de letter T is slechts een vervanging voor ... alles wat we willen dat het is. We moeten alleen T markeren als het bijbehorende type protocol.

Dus nu kunnen we dit protocol gebruiken voor elk modelobject dat we hebben.

1. Artikelrepository

De compiler leidt het type T af naar artikel, omdat we door het implementeren van de methoden hebben aangegeven wat T is. In dit geval een artikelobject.

2. Gebruikersrepository

Dat is het.

Ik hoop dat je het artikel leuk vond en als je vragen of opmerkingen hebt, stel ze gewoon hieronder of neem contact met me op via Twitter en laten we een praatje maken.