Design Patterns: Dependency Injection

Design Mønstre: Dependency Injection
48
Del
10
Del
Dette Cyber ​​mandag Envato Tuts + kurs vil bli redusert til bare $ 3. Ikke gå glipp av.

Selv om avhengighet injeksjon er et tema som sjelden lært til nybegynnere, er det et design mønster som fortjener mer oppmerksomhet. Mange utviklere unngå avhengighet injeksjon, fordi de ikke vet hva det betyr, eller fordi de tror at de ikke trenger det.

I denne artikkelen, jeg kommer til å prøve å overbevise deg om verdien av avhengighet injeksjon. For å gjøre dette, vil jeg introdusere deg til avhengighet injeksjon ved å vise deg hvor enkelt det er i sin enkleste form.

1. Hva er Dependency Injection?

Mye har vært skrevet om avhengighet injeksjon, og det er en haug med verktøy og biblioteker som tar sikte på å forenkle avhengighet injeksjon. Det er ett sitat, men at fanger forvirringen mange mennesker har rundt avhengighet injeksjon.
«Dependency Injection" er en 25-dollar betegnelse for en 5-cent-konseptet. - James Shore

Når du forstå ideen som ligger til grunn avhengighet injeksjon, vil du også forstå sitatet ovenfor. La oss starte med et eksempel for å illustrere konseptet.

En iOS søknad har mange avhengigheter, og søknaden kan stole på avhengig du er ikke engang klar over, er at du ikke anser dem avhengigheter. Følgende kodebiten viser gjennomføringen av en UIViewController underklasse heter ViewController. Gjennomføringen omfatter en metode som heter saveList :. Kan du spot avhengighet
#import "ViewController.h"@interface ViewController () @ enden @ implementering ViewController- (void) saveList:? (NSArray *) liste {if ([list isKindOfClass: [NSArray klasse]]) {NSUserDefaults * userDefaults = [NSUserDefaults standardUserDefaults]; [userDefaults setObject: liste Forkey: @ "liste"]; }} @ slutten

De mest oversett avhengigheter er de som vi er avhengige av det mest. I saveList: metode, lagrer vi en matrise i bruker mislighold database, tilgjengelig gjennom NSUserDefaults klassen. Vi tilgang til delte mislighold objekt ved å påberope seg standardUserDefaults metoden. Hvis du er litt kjent med iOS eller OS X utvikling, da er du sannsynligvis til å bli kjent med NSUserDefaults klassen.

Lagring av data i bruker mislighold databasen er raskt, enkelt og pålitelig. Takket være standardUserDefaults metoden, har vi tilgang til brukerstandarder database fra hvor som helst i prosjektet. Metoden returnerer en singel som vi kan bruke når og hvor vi vil. Livet kan være vakker.

Singleton? Når og hvor? Du lukter noe på? Ikke bare har jeg lukter en avhengighet, jeg også lukte en dårlig praksis. I denne artikkelen, jeg ønsker ikke å åpne opp en boks med ormer med å diskutere bruk og misbruk av enkeltfødte, men det er viktig å forstå at enkeltfødte bør brukes med måte.

De fleste av oss har blitt så brukes til brukerstandarder database som vi ikke ser det som en avhengighet. Men det er absolutt en. Det samme gjelder for varslingssenteret, som vi vanligvis tilgang til gjennom singleton tilgjengelig gjennom defaultCenter metoden. Ta en titt på følgende eksempel for avklaring
#import "ViewController.h"@interface ViewController () @ enden @ implementering ViewController # pragma mark -. # Pragma mark Initialization- (instancetype) init {selv = [super init]; if (egen-) {NSNotificationCenter * nc = [NSNotificationCenter defaultCenter]; [nc addObserver bolig: Velger:selector (applicationWillEnterForeground :) navn: UIApplicationWillEnterForegroundNotification objekt: null]; } Tilbake selv;} # pragma mark - # pragma mark Memory Management (void) dealloc {[[NSNotificationCenter defaultCenter] removeObserver bolig:];} # pragma mark - # pragma mark Notification håndterings- (void) applicationWillEnterForeground: (NSNotification *) varsling {//...} @ slutten

Ovennevnte scenario er svært vanlig. Vi legger til visningen kontrolleren som observatør for meldinger med navn UIApplicationWillEnterForegroundNotification og fjerne det som observatør i dealloc metoden i klassen. Dette legger en annen avhengighet til ViewController klassen, en avhengighet som ofte blir oversett-eller ignorert.

Spørsmålet du kan spørre deg selv er "Hva er problemet?" eller bedre "Er det et problem?" La oss starte med det første spørsmålet.

Hva er problemet?

Basert på eksemplene ovenfor, kan det virke som om det er noe problem. Det er ikke helt sant skjønt. Utsikten controller avhenger delte mislighold protestere og standard varslingssenteret for å gjøre sitt arbeid.

Er det et problem? Nesten hver gjenstand er avhengig av andre objekter for å gjøre sitt arbeid. Problemet er at avhengig er implisitt. En utvikler ny til prosjektet ikke vet visningen kontrolleren er avhengig av disse avhengigheter ved å undersøke klassen grensesnitt.

Teste ViewController klassen vil også vise seg å være vanskelig siden vi ikke har kontroll over NSUserDefaults og NSNotificationCenter klasser . La oss se på noen løsninger på dette problemet. Med andre ord, la oss se hvordan avhengighet injeksjon kan hjelpe oss å løse dette problemet.

2. Injisering Avhengig

Som jeg nevnte i innledningen, er avhengighet injeksjon et meget enkelt konsept. James Shore har skrevet en stor artikkel om enkelhet av avhengighet injeksjon. Det er ett annet sitat fra James Shore om hva avhengighet injeksjon er på sitt aller kjerne.
Avhengighet injeksjon betyr å gi et objekt sine instansvariabler. Egentlig. Det er alt. - James Shore

Det finnes en rekke måter å gjøre dette, men det er viktig å først forstå hva sitatet ovenfor betyr. La oss se hvordan vi kan bruke dette til ViewController klassen.

I stedet for å få tilgang til standard varslingssenteret i init-metoden gjennom defaultCenter klassen metoden skaper vi en eiendom for varslingssenteret i ViewController klassen. Dette er hva den oppdaterte grensesnittet av ViewController klassen ser ut etter denne tillegg
#import < UIKit /UIKit.h >interface ViewController. UIViewController @ eiendom (svak, nonatomic) NSNotificationCenter * defaultCenter;end

Dette betyr også at vi må gjøre litt ekstra arbeid når vi initial en forekomst av ViewController klassen. Som James skriver, late vi ViewController eksempel sine instansvariabler. Det er hvor enkelt avhengighet injeksjon er. Det er et fancy navn for en grei konsept
//Initial Vis ControllerViewController * viewController = [[ViewController Alloc] init]; //Konfigurer View Controller [viewController setNotificationCenter: [NSNotificationCenter defaultCenter]];.

Som et resultat av denne endringen, gjennomføring av de ViewController klasse endringer. Dette er hva de init og dealloc metoder se ut når injisere standard varslingssenteret Anmeldelser #pragma mark - # pragma mark Initialization- (instancetype) init {selv = [super init].; if (egen-) {[self.notificationCenter addObserver bolig: Velger:selector (applicationWillEnterForeground :) navn: UIApplicationWillEnterForegroundNotification objekt: null]; } Avkastning selv;} # pragma mark - # pragma mark Memory Management (void) dealloc {[_notificationCenter removeObserver bolig:];}

Merk at vi ikke bruker selv i dealloc metoden. Dette regnes som en dårlig praksis, siden det kan føre til uventede resultater.

Det er ett problem. Kan du få øye på det? I initializer av ViewController klassen, vi tilgang til notificationCenter eiendom for å legge visningen kontrolleren som observatør. Under initialisering imidlertid notificationCenter eiendommen er ikke fastsatt ennå. Vi kan løse dette problemet ved å føre avhengighet som en parameter for den initializer. Dette er hva som ser ut som Anmeldelser #pragma mark - # pragma mark Initialization- (instancetype) initWithNotificationCenter: (NSNotificationCenter *) notificationCenter {selv = [super init];. if (egen-) {//Set varselsenter [selv setNotificationCenter: notificationCenter]; //Legg Observer [self.notificationCenter addObserver bolig: Velger:selector (applicationWillEnterForeground :) navn: UIApplicationWillEnterForegroundNotification objekt: null]; } Returnere selv;}

For å gjøre dette arbeidet, trenger vi også å oppdatere grensesnittet av ViewController klassen. Vi utelater notificationCenter eiendommen og legge til en metode erklæring for initializer vi skapt
#import < UIKit /UIKit.h >interface ViewController. UIViewController # pragma mark - # pragma mark Initialization- (instancetype) initWithNotificationCenter: (NSNotificationCenter *) notificationCenter;end

I gjennomføringen filen, skaper vi en klasse forlengelse der erklærer vi notificationCenter eiendommen. Ved å gjøre dette, er notificationCenter eiendommen privat til ViewController klasse og kan bare stilles ved å påberope seg nye initializer. Dette er en annen beste praksis å huske på, bare utsetter egenskaper som må være offentlig
#import "ViewController.h"@interface ViewController ()property (sterk, nonatomic) NSNotificationCenter * notificationCenter;.end

på å bruke en forekomst av ViewController klassen, vi er avhengige av initializer vi opprettet tidligere
//Initial Vis ControllerViewController * viewController = [[ViewController alloc] initWithNotificationCenter: [NSNotificationCenter defaultCenter]];
3.. Fordeler

Hva har vi oppnådd ved å eksplisitt injisere varslingssenteret objektet som en avhengighet?

Clarity

Grensesnittet i ViewController klassen utvetydig viser at klassen er avhengig eller avhenger
NSNotificationCenter klassen. Hvis du er ny til iOS eller OS X utvikling, kan dette virke som en liten seier for kompleksiteten vi lagt. Men etter hvert som prosjektene blir mer komplekse, vil du lære å sette pris på hver bit av klarhet du kan legge til et prosjekt. Eksplisitt erklærte avhengig vil hjelpe deg med dette.

Modularitet

Når du begynner å bruke avhengighet injeksjon, vil koden bli mye mer modulær. Selv om vi injiseres en spesifikk klasse inn i ViewController klassen, er det mulig å injisere et objekt som er i samsvar med en bestemt protokoll. Hvis du adoptere denne tilnærmingen, vil det bli mye lettere å erstatte en implementering med en annen
#import < UIKit /UIKit.h >interface ViewController. UIViewController @ eiendom (sterk, nonatomic) id < MyProtocol > someObject; #pragma mark - # pragma mark Initialization- (instancetype) initWithNotificationCenter: (NSNotificationCenter *) notificationCenter;end

I ovennevnte grensesnittet av ViewController klassen, erklærer vi en annen avhengighet. Avhengigheten er et objekt som er i samsvar med den MyProtocol protokollen. Det er der den sanne kraft av avhengighet injeksjon blir tydelig. Den ViewController klassen bryr seg ikke om hvilken type someObject, den bare ber om at det vedtar MyProtocol protokollen. Dette gir svært modulær, fleksibel og testbar kode.

Testing

Selv om testing er ikke så utbredt blant iOS og OS X utviklere som det er i andre samfunn, er testing en nøkkel tema som får økende betydning og popularitet. Ved å vedta avhengighet injeksjon, vil du gjøre koden mye enklere å teste. Hvordan vil du teste følgende initializer? Det kommer til å være vanskelig. ? Høyre
#pragma mark - # pragma mark Initialization- (instancetype) init {selv = [super init]; if (egen-) {NSNotificationCenter * nc = [NSNotificationCenter defaultCenter]; [nc addObserver bolig: Velger:selector (applicationWillEnterForeground :) navn: UIApplicationWillEnterForegroundNotification objekt: null]; } Returnere selv;}

Den andre initializer, men tar denne oppgaven mye enklere. Ta en titt på initializer og testen som går med det Anmeldelser #pragma mark - # pragma mark Initialization- (instancetype) initWithNotificationCenter: (NSNotificationCenter *) notificationCenter {selv = [super init];. if (egen-) {//Set varselsenter [selv setNotificationCenter: notificationCenter]; //Legg Observer [self.notificationCenter addObserver bolig: Velger:selector (applicationWillEnterForeground :) navn: UIApplicationWillEnterForegroundNotification objekt: null]; } Tilbake selv;} # pragma mark - # Pragma mark tester for Initialization- (void) testInitWithNotificationCenter {//Opprett Mock varselsenter id mockNotificationCenter = OCMClassMock ([NSNotificationCenter klasse]); //Initial View Controller ViewController * viewController = [[ViewController alloc] initWithNotificationCenter: mockNotificationCenter]; XCTAssertNotNil (viewController, @ "Utsikten controller bør ikke være null."); OCMVerify ([mockNotificationCenter addObserver: viewController Velger:selector (applicationWillEnterForeground :) navn: UIApplicationWillEnterForegroundNotification objekt: null]);}

Testen ovenfor gjør bruk av OCMock bibliotek, et utmerket tentamen bibliotek for Objective-C. I stedet for å passere i en forekomst av NSNotificationCenter klassen, vi passerer i en mock objekt og verifisere om metodene som må påberopes initializer er faktisk påberopes.

Det er flere måter å teste varslings håndtering og dette er-langt-det enkleste jeg har kommet over. Det legger litt av overhead ved å injisere varslingssenteret objektet som en avhengighet, men fordelen oppveier den ekstra kompleksiteten i min mening.

4. Tredjeparts Solutions

Jeg håper jeg har overbevist deg om at avhengighet injeksjon er et enkelt konsept med en enkel løsning. Det er imidlertid en rekke populære rammer og biblioteker som mål å gjøre avhengighet injeksjon kraftigere og enklere å administrere for komplekse prosjekter. De to mest populære biblioteker er Typhoon og innvending.

Hvis du er ny til avhengighet injeksjon, så anbefaler jeg på det sterkeste til å begynne å bruke teknikker som er skissert i denne opplæringen. Du må først riktig forstå konseptet før du stoler på en tredjepart løsning, slik som Typhoon eller innvending.

Konklusjon

Målet med denne artikkelen var å gjøre avhengighet injeksjon lettere å forstå for folk som er nye for programmering og ukjent med konseptet. Jeg håper jeg har overbevist deg om verdien av avhengighet injeksjon og enkelhet av den underliggende ideen.

Det finnes en rekke gode ressurser om avhengighet injeksjon. James Shore artikkel om avhengighet injeksjon er et must-read for alle utviklere. Graham Lee skrev også en stor artikkel rettet mot iOS og OS X utviklere.