zonder dependency injectionEdit
in het volgende Java-voorbeeld bevat de Client class een Service member variabele die wordt geïnitialiseerd door de Client constructor. De klant controleert welke uitvoering van de dienst wordt gebruikt en controleert de bouw ervan. In deze situatie, de klant wordt gezegd dat een hard-gecodeerde afhankelijkheid van ExampleService.
// 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 is een alternatieve techniek om de member variabele te initialiseren in plaats van expliciet een service object te creëren zoals hierboven getoond., We kunnen dit voorbeeld aanpassen met behulp van de verschillende technieken beschreven en geïllustreerd in de subsecties hieronder.
types of dependency injectionEdit
Er zijn ten minste drie manieren waarop een client-object een verwijzing naar een externe module kan ontvangen:
constructor injection de afhankelijkheden worden geleverd via de class constructor van een client. setter injection de client legt een setter methode bloot die de injector gebruikt om de afhankelijkheid te injecteren. interface injection de interface van de afhankelijkheid biedt een injector methode die de afhankelijkheid zal injecteren in een client doorgegeven aan het., Clients moeten een interface implementeren die een setter-methode blootlegt die de afhankelijkheid accepteert.
andere typesEdit
het is mogelijk dat DI-frameworks andere typen injectie hebben dan hierboven beschreven.
Testkaders kunnen ook andere typen gebruiken. Sommige moderne testkaders vereisen zelfs niet dat clients actief afhankelijkheid injectie accepteren, waardoor legacy code testbaar. Met name in de Java-taal is het mogelijk om reflectie te gebruiken om private attributen openbaar te maken bij het testen en dus injecties door toewijzing te accepteren.,
sommige pogingen tot inversie van de controle bieden geen volledige verwijdering van afhankelijkheid, maar vervangen simpelweg een vorm van afhankelijkheid door een andere. Als vuistregel geldt dat als een programmeur alleen naar de client code kan kijken en kan vertellen welk framework wordt gebruikt, de client een hard-gecodeerde afhankelijkheid heeft van het framework.
Constructor injectionEdit
Deze methode vereist dat de client een parameter in een constructor voor de afhankelijkheid geeft.
Setter injectionEdit
Deze methode vereist dat de client een setter methode voor de afhankelijkheid.,
Interface injectionEdit
Dit is gewoon de client die een rol-interface publiceert naar de setter-methoden van de afhankelijkheden van de client. Het kan worden gebruikt om vast te stellen hoe de injector met de cliënt zou moeten praten wanneer het inspuiten afhankelijkheden.
// 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
voorkeur wanneer alle afhankelijkheden eerst kunnen worden geconstrueerd omdat het kan worden gebruikt om ervoor te zorgen dat het client-object altijd in een geldige staat is, in tegenstelling tot het feit dat sommige afhankelijkheidsreferenties null zijn (niet worden ingesteld)., Op zichzelf ontbreekt het echter aan de flexibiliteit om zijn afhankelijkheden later te laten veranderen. Dit kan een eerste stap zijn naar het maken van de klant onveranderlijk en dus draad veilig.
Setter injectie comparisonEdit
vereist dat de client een setter methode voor elke afhankelijkheid. Dit geeft de vrijheid om de toestand van de afhankelijkheidsreferenties op elk moment te manipuleren. Dit biedt flexibiliteit, maar als er meer dan één afhankelijkheid moet worden geïnjecteerd, is het moeilijk voor de cliënt om ervoor te zorgen dat alle afhankelijkheden worden geïnjecteerd voordat de cliënt voor gebruik kan worden verstrekt.,
omdat deze injecties onafhankelijk gebeuren, is er geen manier om te zeggen wanneer de injector klaar is met het bedraden van de client. Een afhankelijkheid kan gewoon nul worden gelaten door de injector niet aan te roepen zijn setter. Dit dwingt de controle dat de injectie is voltooid vanaf het moment dat de cliënt wordt geassembleerd tot wanneer het wordt gebruikt.
Interface injection comparisonEdit
het voordeel van interface injection is dat afhankelijkheden volledig onbekend kunnen zijn met hun clients, maar toch een verwijzing naar een nieuwe client kunnen ontvangen en, met behulp daarvan, een referentie-naar-zelf terug kunnen sturen naar de client., Op deze manier worden de afhankelijkheden injectoren. De sleutel is dat de injectiemethode (die gewoon een klassieke setter-methode zou kunnen zijn) wordt verstrekt via een interface.
een assembler is nog steeds nodig om de client en zijn afhankelijkheden te introduceren. De assembler zou een referentie naar de client nemen, casten naar de setter interface die die afhankelijkheid instelt, en het doorgeven aan dat dependency object dat zou omdraaien en een referentie-naar-zelf terug te geven aan de client.,
wil interface-injectie waarde hebben, dan moet de afhankelijkheid iets doen naast simpelweg een verwijzing naar zichzelf doorgeven. Dit zou kunnen fungeren als een fabriek of sub-assembler om andere afhankelijkheden op te lossen, en dus enkele details van de hoofd assembler abstraheren. Het zou referentie-tellen kunnen zijn, zodat de afhankelijkheid weet hoeveel klanten het gebruiken. Als de afhankelijkheid een verzameling clients onderhoudt, kan het ze later allemaal injecteren met een andere instantie van zichzelf.,
voorbeelden van het assembleren edit
handmatig assembleren in het hoofd met de hand is een manier om dependency injection te implementeren.
het voorbeeld hierboven maakt de objectgrafiek handmatig en roept het dan op een bepaald punt aan om het te laten werken. Belangrijk om op te merken is dat deze injector niet zuiver is. Het gebruikt een van de objecten die het construeert. Het heeft een puur construction-only relatie met ExampleService, maar mengt de bouw en het gebruik van de klant. Dit zou niet gebruikelijk moeten zijn. Het is echter onvermijdelijk., Net zoals objectgeoriënteerde software een niet-objectgeoriënteerde statische methode zoals main() nodig heeft om te beginnen, heeft een afhankelijkheid geïnjecteerde objectgrafiek minstens één (bij voorkeur slechts één) ingangspunt nodig om de hele zaak op gang te krijgen.
handmatige constructie in de hoofdmethode is misschien niet zo eenvoudig en kan ook betrekking hebben op het aanroepen van bouwers, fabrieken of andere bouwpatronen. Dit kan vrij geavanceerd en abstract zijn., De lijn wordt overschreden van handmatige dependency injection naar framework dependency injection zodra de constructiecode niet langer aangepast is aan de toepassing en in plaats daarvan universeel is.
Frameworks zoals Spring kunnen dezelfde objecten construeren en ze aan elkaar verbinden voordat een referentie naar de client wordt geretourneerd. Alle vermelding van de concrete Voorbeeldservice kan worden verplaatst van de code naar de configuratiegegevens.
Frameworks zoals Spring staan assembly details toe om geëxternaliseerd te worden in configuratiebestanden.Deze code (hierboven) construeert objecten en verbindt ze aan elkaar volgens bonen.xml (hieronder)., ExampleService is nog steeds gebouwd, hoewel het alleen hieronder vermeld. Een lange en complexe objectgrafiek kan op deze manier worden gedefinieerd en de enige klasse die in code wordt vermeld, is die met de entry point methode, die in dit geval greet () is.
in het bovenstaande voorbeeld hebben Client en Service geen wijzigingen hoeven te ondergaan die vóór het voorjaar moeten worden geleverd. Ze mogen eenvoudige POJOs blijven. Dit laat zien hoe de lente kan verbinden diensten en klanten die volledig onwetend van het bestaan zijn. Dit kon niet worden gezegd als de lente annotaties werden toegevoegd aan de klassen., Door te voorkomen dat Lente-specifieke annotaties en oproepen zich verspreiden onder vele klassen, blijft het systeem slechts losjes afhankelijk van de lente. Dit kan belangrijk zijn als het systeem de lente wil overleven.
De keuze om POJOs zuiver te houden is niet zonder kosten. In plaats van de moeite te besteden om complexe configuratiebestanden te ontwikkelen en te onderhouden, is het mogelijk om eenvoudig annotaties te gebruiken om klassen te markeren en de lente de rest van het werk te laten doen. Het oplossen van afhankelijkheden kan eenvoudig zijn als ze een conventie volgen, zoals matching op type of op naam. Dit is het kiezen van conventie boven configuratie., Het is ook betwistbaar dat, wanneer het refactoring aan een ander kader, het verwijderen van kaderspecifieke annotaties een triviaal deel van de taak zou zijn en vele injectieannotaties zijn nu gestandaardiseerd.
@Componentpublic class ExampleService { public String getName() { return "World!"; }}
Assemblagevergelijkingdit
de verschillende injectorimplementaties (fabrieken, service locators en dependency injection containers) zijn niet zo verschillend wat de dependency injection betreft. Wat het verschil maakt is waar ze mogen worden gebruikt., Verplaats oproepen naar een fabriek of een service locator uit de client en naar main en plotseling main maakt een vrij goede afhankelijkheid injectie container.
door alle kennis van de injector weg te halen, blijft een schone cliënt, vrij van kennis van de buitenwereld, achter. Elk object dat andere objecten gebruikt, kan echter als een client worden beschouwd. Het object dat main bevat is geen uitzondering. Dit hoofdobject Gebruikt geen dependency injection. Het is eigenlijk met behulp van de service locator patroon. Dit kan niet worden vermeden omdat de keuze van de dienst implementaties ergens moet worden gemaakt.,
het externaliseren van de afhankelijkheden in configuratiebestanden verandert dit feit niet. Wat maakt deze realiteit deel uit van een goed ontwerp is dat de service locator is niet verspreid over de code base. Het is beperkt tot één plaats per toepassing. Dit laat de rest van de code base vrij om afhankelijkheid injectie te gebruiken om schone clients te maken.
Dependency Injection PatternEdit
De voorbeelden tot nu toe waren al te eenvoudige voorbeelden over het maken van een tekenreeks., Echter, de afhankelijkheid injectie patroon is het meest nuttig bij het construeren van een object grafiek waar objecten communiceren via berichten. Objecten gebouwd in main zal duren voor de levensduur van het programma. Het typische patroon is om de grafiek te construeren en dan één methode op één object aan te roepen om de stroom van controle in de objectgrafiek te sturen. Net zoals main het toegangspunt tot de statische code is, is deze ene methode het toegangspunt tot de toepassingen niet-statische code.,
AngularJS exampledit
in het AngularJS framework zijn er slechts drie manieren waarop een component (object of functie) direct toegang kan krijgen tot zijn afhankelijkheden:
- Het component kan de afhankelijkheid creëren, meestal met behulp van de
new
operator. - de component kan de afhankelijkheid opzoeken door te verwijzen naar een globale variabele.
- de component kan de afhankelijkheid doorgeven waar het nodig is.
de eerste twee opties voor het maken of opzoeken van afhankelijkheden zijn niet optimaal omdat ze de afhankelijkheid van de component hard coderen., Dit maakt het moeilijk, zo niet onmogelijk, om de afhankelijkheden te wijzigen. Dit is vooral problematisch in tests, waar het vaak wenselijk is om proefafhankelijkheden voor testisolatie te bieden.
de derde optie is de meest haalbare, omdat het de verantwoordelijkheid van het lokaliseren van de afhankelijkheid van de component verwijdert. De afhankelijkheid wordt gewoon overgedragen aan de component.
in het bovenstaande voorbeeld houdt SomeClass
zich niet bezig met het maken of lokaliseren van de groeter-afhankelijkheid, het wordt gewoon de groeter overhandigd wanneer het wordt geïnstalleerd.,
Dit is wenselijk, maar het legt de verantwoordelijkheid om de afhankelijkheid van de code die SomeClass
construeert, in handen te krijgen.
om de verantwoordelijkheid van dependency creation te beheren, heeft elke AngularJS toepassing een injector. De injector is een service locator die verantwoordelijk is voor de bouw en het opzoeken van afhankelijkheden.
Hier is een voorbeeld van het gebruik van de injector service:
Maak een nieuwe injector aan die componenten kan leveren die gedefinieerd zijn in de myModule
module en vraag onze greeter service aan bij de injector., (Dit wordt meestal automatisch gedaan door de AngularJS bootstrap).
var injector = angular.injector();var greeter = injector.get('greeter');
vragen naar afhankelijkheden lost het probleem van harde codering op, maar het betekent ook dat de injector door de hele toepassing moet worden doorgegeven. Het passeren van de injector breekt de wet van Demeter., Om dit te verhelpen gebruiken we een declaratieve notatie in onze HTML-sjablonen, om de verantwoordelijkheid voor het aanmaken van Componenten over te dragen aan de injector, zoals in dit voorbeeld:
<div ng-controller="MyController"> <button ng-click="sayHello()">Hello</button></div>
function MyController($scope, greeter) { $scope.sayHello = function() { greeter.greet('Hello World'); };}
wanneer AngularJS de HTML compileert, verwerkt het de ng-controller
richtlijn, die op zijn beurt de injector vraagt om een instantie van de controller en zijn afhankelijkheden aan te maken.
injector.instantiate(MyController);
Dit wordt allemaal achter de schermen gedaan., Omdat de ng-controller
uitstelt naar de injector om de klasse te installeren, kan het voldoen aan alle afhankelijkheden van MyController
zonder dat de controller ooit van de injector Weet. De toepassingscode verklaart eenvoudig de afhankelijkheden die het nodig heeft, zonder te hoeven omgaan met de injector. Deze opstelling is niet in strijd met de wet van Demeter.
C#Bewerk
voorbeeld van de injectie van de Constructor, Setter en Interface op C #