uden afhængighedsinjektionredit
i det følgende Java-eksempel indeholder Klientklassen en servicemedlemsvariabel, der initialiseres af Klientkonstruktøren. Klienten styrer hvilken implementering af service der bruges og styrer dens konstruktion. I denne situation siges klienten at have en hårdkodet afhængighed af Eksempelservice.
// An example without dependency injectionpublic class Client { // Internal reference to the service used by this client private ExampleService service; // Constructor Client() { // Specify a specific implementation in the constructor instead of using dependency injection service = new ExampleService(); } // Method within this client that uses the services public String greet() { return "Hello " + service.getName(); }}
Dependency injection er en alternativ teknik til at initialisere den pågældende variabel snarere end eksplicit skabe en service, objektet, som vist ovenfor., Vi kan justere dette eksempel ved hjælp af de forskellige teknikker, der er beskrevet og illustreret i underafsnittene nedenfor.
typer af afhængighed injectionEdit
Der er mindst tre måder en klient objekt kan modtage en henvisning til et eksternt modul:
constructor injection afhængighederne leveres gennem en klients klasse constructor. setter injektion klienten udsætter en setter metode, injektoren bruger til at injicere afhængigheden. interfaceinjektion afhængighedens interface giver en injektormetode, der vil injicere afhængigheden i enhver klient, der overføres til den., Klienter skal implementere en grænseflade, der udsætter en setter-metode, der accepterer afhængigheden.
andre typeredit
det er muligt for DI-rammer at have andre typer injektion ud over dem, der er præsenteret ovenfor.
test rammer kan også bruge andre typer. Nogle moderne testrammer kræver ikke engang, at klienter aktivt accepterer afhængighedsinjektion, hvilket gør arvskode testbar. Især på Java-sproget er det muligt at bruge refleksion til at offentliggøre private attributter, når de testes og således acceptere injektioner ved tildeling.,
nogle forsøg på Inversion af kontrol giver ikke fuld fjernelse af afhængighed, men erstatter i stedet blot en form for afhængighed for en anden. Som en tommelfingerregel, Hvis en programmør kun kan se på klientkoden og fortælle, hvilken ramme der bruges, har klienten en hårdkodet afhængighed af rammen.
Constructor injectionEdit
denne metode kræver, at klienten leverer en parameter i en konstruktør for afhængigheden.
Setter injectionEdit
denne metode kræver, at klienten leverer en setter-metode til afhængigheden.,
Interface injectionEdit
Dette er simpelthen klienten, der offentliggør en rollegrænseflade til settermetoderne for klientens afhængigheder. Det kan bruges til at fastslå, hvordan injektoren skal tale med klienten, når han injicerer afhængigheder.
// Service setter interface.public interface ServiceSetter { public void setService(Service service);}// Client classpublic class Client implements ServiceSetter { // Internal reference to the service used by this client. private Service service; // Set the service that this client is to use. @Override public void setService(Service service) { this.service = service; }}
Constructor injection comparisonEdit
Foretrukne, når alle afhængigheder kan være konstrueret det første, fordi det kan bruges til at sikre kunden objekt er altid i en gyldig tilstand, som modsætning til at have nogle af sin afhængighed henvisninger være null (ikke indstillet)., På egen hånd mangler det imidlertid fleksibiliteten til at få sine afhængigheder ændret senere. Dette kan være et første skridt i retning af at gøre klienten uforanderlig og derfor tråd sikker.
setter injection comparisonEdit
kræver, at klienten leverer en setter-metode for hver afhængighed. Dette giver friheden til at manipulere tilstanden af afhængighedsreferencerne til enhver tid. Dette giver fleksibilitet, men hvis der er mere end en afhængighed, der skal injiceres, er det vanskeligt for klienten at sikre, at alle afhængigheder injiceres, før klienten kunne leveres til brug.,da disse injektioner sker uafhængigt, er der ingen måde at fortælle, hvornår injektoren er færdig med at forbinde klienten. En afhængighed kan efterlades null ved blot at injektoren ikke kalder sin setter. Dette tvinger kontrollen af, at injektionen blev afsluttet, fra klienten er samlet til, når den bruges.
interface injection comparisonEdit
fordelen ved interface injection er, at afhængigheder kan være helt uvidende om deres klienter, men alligevel stadig kan modtage en henvisning til en ny klient og ved hjælp af den sende en reference til sig selv tilbage til klienten., På denne måde bliver afhængighederne injektorer. Nøglen er, at injektionsmetoden (som bare kunne være en klassisk setter-metode) leveres via en grænseflade.
en assembler er stadig nødvendig for at introducere klienten og dens afhængigheder. Assembleren ville tage en henvisning til klienten, kaste den til setter-grænsefladen, der indstiller denne afhængighed, og videregive den til det afhængighedsobjekt, der ville vende rundt og videregive en reference-til-selv tilbage til klienten.,
for at interfaceinjektion skal have værdi, skal afhængigheden gøre noget ud over blot at sende en henvisning til sig selv tilbage. Dette kunne fungere som en fabrik eller sub-assembler til at løse andre afhængigheder, således abstrahere nogle detaljer fra de vigtigste assembler. Det kan være referencetælling, så afhængigheden ved, hvor mange klienter der bruger det. Hvis afhængigheden opretholder en samling af klienter, kan den senere injicere dem alle med en anden forekomst af sig selv.,
montering af eksempleredit
manuel montering i hoved for hånd er en måde at implementere afhængighedsinjektion på.
eksemplet ovenfor konstruerer objektgrafen manuelt og påkalder den derefter på et tidspunkt for at starte den med at fungere. Vigtigt at bemærke er, at denne injektor ikke er ren. Det bruger et af de objekter, det konstruerer. Det har et rent konstruktion-kun forhold til e .ampleservice, men blander konstruktion og brug af klient. Dette bør ikke være almindeligt. Det er dog uundgåeligt., Ligesom objektorienteret soft .are har brug for en ikke-objektorienteret statisk metode som main() for at komme i gang, har en afhængighedsindsprøjtet objektgraf mindst et (helst kun et) indgangspunkt for at få det hele startet.
Manuel konstruktion i hovedmetoden er muligvis ikke lige frem og kan også involvere opkald til bygherrer, fabrikker eller andre byggemønstre. Dette kan være ret avanceret og abstrakt., Linjen krydses fra Manuel afhængighedsinjektion til rammeafhængighedsinjektion, når konstruktionskoden ikke længere er tilpasset applikationen og i stedet er universel.
rammer som foråret kan konstruere de samme objekter og wireire dem sammen, før du returnerer en henvisning til klienten. Al omtale af det konkrete Eksempeltjenesten kan flyttes fra koden til konfigurationsdataene.
rammer som forår tillader monteringsdetaljer at blive eksternaliseret i konfigurationsfiler.Denne kode (ovenfor) konstruerer objekter og leder dem sammen i henhold til bønner.xml (nedenfor)., Eksempelservice er stadig konstrueret, selv om det kun er nævnt nedenfor. En lang og kompleks objektgraf kan defineres på denne måde, og den eneste klasse, der er nævnt i kode, ville være den med indgangspunktmetoden, som i dette tilfælde er greet().
i eksemplet ovenfor har klient og Service ikke været nødt til at gennemgå nogen ændringer, der skal leveres i foråret. De får lov til at forblive enkle POJOs. Dette viser, hvordan foråret kan forbinde tjenester og kunder, der er helt uvidende om dens eksistens. Dette kunne ikke siges, hvis Forårsbeskrivelser blev tilføjet til klasserne., Ved at holde forårs-specifikke kommentarer og opkald fra at sprede sig blandt mange klasser, forbliver systemet kun løst afhængigt af foråret. Dette kan være vigtigt, hvis systemet har til hensigt at overleve foråret.
valget om at holde POJOs rene kommer ikke uden omkostninger. I stedet for at bruge indsatsen på at udvikle og vedligeholde komplekse konfigurationsfiler, er det muligt blot at bruge annotationer til at markere klasser og lade foråret gøre resten af arbejdet. Det kan være enkelt at løse afhængigheder, hvis de følger en konvention som f.eks. Dette er at vælge konvention frem for konfiguration., Det er også diskutabelt, at når refactoring til en anden ramme, ville fjernelse af rammespecifikke annotationer være en triviel del af opgaven, og mange injektionsnotationer er nu standardiserede.
@Componentpublic class ExampleService { public String getName() { return "World!"; }}
Forsamling comparisonEdit
De forskellige injector implementeringer (fabrikker, service locators, og dependency injection containere) er ikke så forskellige, så vidt dependency injection er bekymret. Hvad gør hele forskellen er, hvor de får lov til at blive brugt., Flyt opkald til en fabrik eller en service locator ud af klienten og ind i main og pludselig main gør en forholdsvis god afhængighed injektion container.
Ved at flytte al viden om injektoren ud, bliver en ren klient, fri for viden om omverdenen, efterladt. Ethvert objekt, der bruger andre objekter, kan dog betragtes som en klient. Objektet, der indeholder main, er ingen undtagelse. Dette hovedobjekt bruger ikke afhængighedsinjektion. Det er faktisk ved hjælp af service locator mønster. Dette kan ikke undgås, fordi valget af serviceimplementeringer skal foretages et sted.,
eksternalisering af afhængighederne i konfigurationsfiler ændrer ikke denne kendsgerning. Hvad gør denne virkelighed en del af et godt design er, at tjenesten locator ikke er spredt over hele kodebase. Det er begrænset til Onet sted pr ansøgning. Dette efterlader resten af kodebasen fri til at bruge afhængighedsindsprøjtning til at gøre rene klienter.
Afhængighedsindsprøjtning Mønstredit
eksemplerne indtil nu har været alt for enkle eksempler på konstruktion af en streng., Afhængighedsindsprøjtningsmønsteret er dog mest nyttigt, når man konstruerer en objektgraf, hvor objekter kommunikerer via meddelelser. Objekter konstrueret i hoved vil vare i programmets levetid. Det typiske mønster er at konstruere grafen og derefter kalde en metode på et objekt for at sende strømmen af kontrol ind i objektgrafen. Ligesom main er indgangspunktet til den statiske kode, er denne ene metode indgangspunktet til applikationerne ikke-statisk kode.,
AngularJS exampleEdit
I AngularJS ramme, er der kun tre måder, en komponent (objekt eller en funktion) kan få direkte adgang til dens afhængigheder:
- Den komponent, der kan skabe afhængighed, typisk ved brug af
new
operatør. - komponenten kan slå afhængigheden op ved at henvise til en global variabel.
- komponenten kan få afhængigheden overført til den, hvor den er nødvendig.
de to første muligheder for at oprette eller slå op afhængigheder er ikke optimale, fordi de hårdt kode afhængigheden til komponenten., Dette gør det vanskeligt, hvis ikke umuligt, at ændre afhængighederne. Dette er især problematisk i test, hvor det ofte er ønskeligt at tilvejebringe falske afhængigheder til testisolering.
den tredje mulighed er den mest levedygtige, da den fjerner ansvaret for at lokalisere afhængigheden fra komponenten. Afhængigheden overleveres simpelthen til komponenten.
i ovenstående eksempel er SomeClass
ikke bekymret for at oprette eller lokalisere greeter-afhængigheden, det overleveres simpelthen greeter, når det instantieres.,
Dette er ønskeligt, men det sætter ansvaret for at få fat i afhængigheden af koden, der konstruerer SomeClass
.for at styre ansvaret for afhængighedsoprettelse har hver AngularJS-applikation en injektor. Injektoren er en service locator, der er ansvarlig for konstruktion og opslag af afhængigheder.
Her er et eksempel på brug af injektortjenesten:
Opret en ny injektor, der kan levere komponenter defineret i myModule
– modulet og anmode om vores greeter-service fra injektoren., (Dette gøres normalt automatisk af AngularJS bootstrap).
var injector = angular.injector();var greeter = injector.get('greeter');
at bede om afhængigheder løser problemet med hård kodning, men det betyder også, at injektoren skal sendes gennem hele applikationen. At passere injektoren bryder Demeters lov., For at afhjælpe dette, bruger vi en deklarativ notation i vores HTML-skabeloner, til at overlade ansvaret for at skabe komponenter over til injektor, som i dette eksempel:
<div ng-controller="MyController"> <button ng-click="sayHello()">Hello</button></div>
function MyController($scope, greeter) { $scope.sayHello = function() { greeter.greet('Hello World'); };}
Når AngularJS samler HTML -, it-processer ng-controller
direktiv, hvilket igen anmoder injektor for at oprette en instans af controlleren og dens afhængigheder.
injector.instantiate(MyController);
dette gøres alt bag kulisserne., Fordi ng-controller
udskyder injektoren for at instantiere klassen, kan den tilfredsstille alle afhængighederne af MyController
uden at controlleren nogensinde ved om injektoren. Ansøgningskoden erklærer simpelthen de afhængigheder, den har brug for, uden at skulle håndtere injektoren. Denne opsætning bryder ikke loven i Demeter.
C#Edit
eksempel på Konstruktørinjektion, Setter-injektion og Interfaceinjektion på C #