utan dependency injectionEdit
i följande Java-exempel innehåller Klientklassen en tjänstmedlemvariabel som initieras av Klientkonstruktören. Kunden kontrollerar vilken implementering av Tjänsten som används och kontrollerar dess konstruktion. I denna situation sägs kunden ha ett hårdkodat beroende av Exempelservice.
// 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 är en alternativ teknik för att initiera medlemsvariabeln i stället för att uttryckligen skapa ett serviceobjekt som visas ovan., Vi kan justera detta exempel med hjälp av de olika tekniker som beskrivs och illustreras i underavsnitten nedan.
typer av beroendeinjektionedit
det finns minst tre sätt som ett klientobjekt kan få en referens till en extern modul:
konstruktorinjektion beroendet tillhandahålls via en klients klasskonstruktör. setter-injektion klienten exponerar en setter-metod som injektorn använder för att injicera beroendet. interface injection dependency gränssnitt ger en injektor metod som kommer att injicera beroendet i någon klient skickas till den., Klienter måste implementera ett gränssnitt som exponerar en setter-metod som accepterar beroendet.
andra typesEdit
det är möjligt för DI frameworks att ha andra typer av injektion utöver de som presenteras ovan.
Testramar kan också använda andra typer. Vissa moderna testramar kräver inte ens att klienter aktivt accepterar beroendeinjektion, vilket gör äldre kod testbar. I synnerhet på Java-språket är det möjligt att använda reflektion för att göra privata attribut offentliga vid testning och därmed Acceptera injektioner genom uppdrag.,
vissa försök till Inversion av kontrollen ger inte fullständigt borttagande av beroendet, utan ersätter helt enkelt en form av beroende för en annan. Som en tumregel, om en programmerare kan titta på ingenting annat än klientkoden och berätta vilken ram som används, har klienten ett hårdkodat beroende av ramverket.
Constructor injectionEdit
den här metoden kräver att klienten tillhandahåller en parameter i en konstruktör för beroendet.
setter injectionEdit
den här metoden kräver att klienten tillhandahåller en setter-metod för beroendet.,
interface injectionEdit
det här är helt enkelt klienten som publicerar ett rollgränssnitt till setter-metoderna för klientens beroenden. Det kan användas för att fastställa hur injektorn ska prata med kunden när man injicerar beroenden.
// 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
föredras när alla beroenden kan konstrueras först eftersom det kan användas för att säkerställa att klientobjektet alltid är i ett giltigt tillstånd, i motsats till att vissa av dess beroende referenser är null (inte ställas in)., Men på egen hand saknar den flexibiliteten att få sina beroenden ändrade senare. Detta kan vara ett första steg mot att göra kunden oföränderlig och därför tråd säker.
setter injection comperisonedit
kräver att klienten tillhandahåller en setter-metod för varje beroende. Detta ger friheten att när som helst manipulera tillståndet för beroendereferenser. Detta ger flexibilitet, men om det finns mer än ett beroende som ska injiceras är det svårt för kunden att se till att alla beroenden injiceras innan kunden kan tillhandahållas för användning.,
eftersom dessa injektioner sker oberoende, finns det inget sätt att berätta när injektorn är klar ledningar klienten. Ett beroende kan lämnas null helt enkelt av injektorn misslyckas med att ringa sin setter. Detta tvingar kontrollen att injektionen slutfördes från när klienten är monterad till när den används.
Interface injection comperisonedit
fördelen med interface injection är att beroenden kan vara helt okunniga om sina kunder men ändå kan få en hänvisning till en ny klient och, med hjälp av den, skicka en referens-till-själv tillbaka till klienten., På detta sätt blir beroenden injektorer. Nyckeln är att injektionsmetoden (som bara kan vara en klassisk setter-metod) tillhandahålls genom ett gränssnitt.
en assembler behövs fortfarande för att introducera klienten och dess beroenden. Assembler skulle ta en hänvisning till klienten, kasta den till setter-gränssnittet som anger det beroendet, och skicka det till det beroendet objektet som skulle vända och skicka en referens-till-själv tillbaka till klienten.,
för att gränssnittsinjektion ska ha värde måste beroendet göra något förutom att helt enkelt skicka tillbaka en hänvisning till sig själv. Detta kan fungera som en fabrik eller undermonterare för att lösa andra beroenden, vilket abstraherar några detaljer från huvudmonteraren. Det kan vara referensräkning så att beroendet vet hur många kunder använder det. Om beroendet upprätthåller en samling kunder kan det senare injicera dem alla med en annan förekomst av sig själv.,
montering examplesEdit
manuellt montering i huvud för hand är ett sätt att genomföra beroendeinsprutning.
exemplet ovan konstruerar objektdiagrammet manuellt och anropar det vid ett tillfälle för att starta det. Viktigt att notera är att denna injektor inte är ren. Den använder ett av objekten den konstruerar. Den har en rent konstruktion-endast relation med Exempelservice men blandar konstruktion och användning av kunden. Detta bör inte vara vanligt. Det är dock oundvikligt., Precis som objektorienterad programvara behöver en icke-objektorienterad statisk metod som main() för att komma igång behöver ett beroendeinjekterat objektdiagram minst en (helst endast en) ingångspunkt för att få hela saken igång.
Manuell konstruktion i huvudmetoden kanske inte är detta rakt fram och kan innebära att man ringer byggare, fabriker eller andra konstruktionsmönster också. Detta kan vara ganska avancerat och abstrakt., Linjen korsas från manuell beroendeinjektion till framework dependency injection när konstruktionskoden inte längre är anpassad till applikationen och är istället universell.
ramverk som Spring kan konstruera samma objekt och koppla ihop dem innan du returnerar en referens till klienten. Allt omnämnande av de konkreta Exempelenservice kan flyttas från koden till konfigurationsdata.
ramverk som Spring tillåter att monteringsdetaljer externaliseras i konfigurationsfiler.Denna kod (ovan) konstruerar objekt och leder dem ihop enligt bönor.xml (nedan)., ExampleService är fortfarande konstruerad även om det bara nämns nedan. Ett långt och komplext objektdiagram kan definieras på detta sätt och den enda klass som nämns i koden skulle vara den med inmatningsmetoden, som i detta fall är hälsning().
i exemplet ovan har kunden och tjänsten inte behövt genomgå några ändringar som ska tillhandahållas av våren. De får förbli enkla POJOs. Detta visar hur våren kan ansluta tjänster och kunder som är helt okunniga om dess existens. Detta kunde inte sägas om vårens anteckningar tillsattes till klasserna., Genom att hålla Vårspecifika anteckningar och samtal från att sprida ut bland många klasser, förblir systemet endast löst beroende av våren. Detta kan vara viktigt om systemet har för avsikt att överleva våren.
valet att hålla POJOs ren kommer inte utan kostnad. I stället för att spendera ansträngningarna att utveckla och underhålla komplexa konfigurationsfiler, är det möjligt att helt enkelt använda Anteckningar för att markera klasser och låta våren göra resten av arbetet. Att lösa beroenden kan vara enkelt om de följer en konvention som matchning efter typ eller namn. Detta är att välja konvention över konfiguration., Det är också argumenterbart att, när refactoring till en annan ram, ta bort ramspecifika anteckningar skulle vara en trivial del av uppgiften och många injektion anteckningar nu standardiseras.
@Componentpublic class ExampleService { public String getName() { return "World!"; }}
Assembly comparisonEdit
de olika injektor implementationer (fabriker, service locators och beroendeinsprutningsbehållare) är inte så olika när det gäller beroendeinsprutning. Det som gör hela skillnaden är var de får användas., Flytta samtal till en fabrik eller en Service locator ut ur klienten och i huvudsak och plötsligt huvud gör en ganska bra beroende injektion Behållare.
genom att flytta all kunskap om injektorn ut, är en ren klient, fri från kunskap om omvärlden, kvar. Alla objekt som använder andra objekt kan dock betraktas som en klient. Objektet som innehåller main är inget undantag. Detta huvudobjekt använder inte beroendeinjektion. Det är faktiskt med hjälp av tjänsten locator mönster. Detta kan inte undvikas eftersom valet av tjänsteimplementeringar måste göras någonstans.,
externalisering av beroenden i konfigurationsfiler ändrar inte detta faktum. Vad som gör denna verklighet del av en bra design är att tjänsten locator inte sprids i hela kodbasen. Det är begränsat till en plats per ansökan. Detta lämnar resten av kodbasen fri att använda beroendeinjektion för att göra rena klienter.
Dependency Injection PatternEdit
exemplen hittills har varit alltför enkla exempel på att bygga en sträng., Beroendeinsprutningsmönstret är dock mest användbart när man konstruerar ett objektdiagram där objekt kommunicerar via meddelanden. Objekt konstruerade i huvudsak kommer att pågå för livet av programmet. Det typiska mönstret är att konstruera grafen och sedan ringa en metod på ett objekt för att skicka flödet av kontroll i objektdiagrammet. Precis som main är ingångspunkten till den statiska koden, är den här metoden ingångspunkten till programmen icke-statisk kod.,
AngularJS exampleEdit
i AngularJS ramverk, det finns bara tre sätt en komponent (objekt eller funktion) kan direkt komma åt dess beroenden:
- komponenten kan skapa beroendet, vanligtvis med hjälp av
new
operatör. - komponenten kan slå upp beroendet genom att hänvisa till en global variabel.
- komponenten kan ha beroendet skickas till den där det behövs.
de två första alternativen för att skapa eller slå upp beroenden är inte optimala eftersom de hårdkodar beroendet till komponenten., Detta gör det svårt, om inte omöjligt, att ändra beroenden. Detta är särskilt problematiskt i tester, där det ofta är önskvärt att tillhandahålla mock-beroenden för testisolering.
det tredje alternativet är det mest lönsamma, eftersom det tar bort ansvaret för att lokalisera beroendet från komponenten. Beroendet lämnas helt enkelt till komponenten.
i exemplet ovan, SomeClass
handlar inte om att skapa eller lokalisera greeter beroende, det är helt enkelt överlämnas greeter när det instansieras.,
det här är önskvärt, men det lägger ansvaret för att få tag på beroendet av koden som konstruerar SomeClass
.
För att hantera ansvaret för att skapa beroende har varje AngularJS-applikation en injektor. Injektorn är en Service locator som ansvarar för konstruktion och utseende av beroenden.
här är ett exempel på att använda injektortjänsten:
skapa en ny injektor som kan tillhandahålla komponenter som definieras i myModule
– modulen och begära vår greeter-tjänst från injektorn., (Detta görs vanligtvis automatiskt av AngularJS bootstrap).
var injector = angular.injector();var greeter = injector.get('greeter');
be om beroenden löser problemet med hårdkodning, men det betyder också att injektorn måste skickas genom hela programmet. Att passera injektorn bryter mot Demeters lag., För att avhjälpa detta använder vi en deklarativ notation i våra HTML-mallar, för att lämna ansvaret för att skapa komponenter över till injektorn, som i det här exemplet:
<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 sammanställer HTML-koden, behandlar det ng-controller
– direktivet, som I turn ber injektorn att skapa en instans av styrenheten och dess beroenden.
injector.instantiate(MyController);
allt detta görs bakom kulisserna., Eftersomng-controller
skjuter upp till injektorn för att instansiera klassen, kan den tillfredsställa alla beroenden avMyController
utan att regulatorn någonsin vet om injektorn. Applikationskoden förklarar helt enkelt de beroenden den behöver, utan att behöva hantera injektorn. Denna inställning bryter inte lagen om Demeter.
C#Edit
exempel på Konstruktorinjektionen, setterinjektionen och Gränssnittsinjektionen på c #