Er zijn al verschillende vragen gepost met specifieke vragen over dependency injection, zoals wanneer je het moet gebruiken en welke frameworks er voor zijn. Echter,
Wat is dependency injection en wanneer/waarom moet het wel of niet gebruikt worden?
De beste definitie die ik tot nu toe heb gevonden is een van James Shore:
"Dependency Injection" is een 25-dollar term voor een 5-cent concept. [...] Dependency injection betekent dat je een object zijn instantie variabelen geeft. [...].
Er is een artikel van Martin Fowler dat ook nuttig kan blijken.
Dependency injection is in feite het leveren van de objecten die een object nodig heeft (zijn dependencies) in plaats van het zelf te laten bouwen. Het is een zeer nuttige techniek voor testen, omdat het toestaat afhankelijkheden te mocken of stubbed uit.
Dependencies kunnen op vele manieren in objecten worden geïnjecteerd (zoals constructor injection of setter injection). Men kan zelfs gespecialiseerde dependency injection frameworks (b.v. Spring) gebruiken om dat te doen, maar die zijn zeker'niet vereist. Je hebt die frameworks niet nodig om dependency injection te hebben. Het expliciet instantiëren en doorgeven van objecten (dependencies) is net zo goed een injectie als injectie door frameworks.
Dependency Injection is het doorgeven van afhankelijkheden aan andere objecten of framework( dependency injector).
Dependency injection maakt testen eenvoudiger. De injectie kan worden gedaan door constructor.
SomeClass()
heeft zijn constructor als volgt:
public SomeClass() {
myObject = Factory.getObject();
}
Probleem:
Als mijnObject
complexe taken met zich meebrengt, zoals schijf- of netwerktoegang, is het moeilijk om een unit test uit te voeren op SomeClass()
. Programmeurs moeten myObject
dan simuleren en kunnen de fabrieksaanroep onderbreken**.
Alternatieve oplossing:
mijnObject
door als argument aan de constructorpublic SomeClass (MyClass myObject) {
this.myObject = myObject;
}
myObject
kan direct worden doorgegeven, wat testen makkelijker maakt.
Het is moeilijker om componenten te isoleren in unit testen zonder dependency injection.
In 2013, toen ik dit antwoord schreef, was dit een belangrijk thema op de Google Testing Blog. Het blijft voor mij het grootste voordeel, omdat programmeurs niet altijd de extra flexibiliteit in hun runtime-ontwerp nodig hebben (bijvoorbeeld voor service locator of soortgelijke patronen). Programmeurs moeten vaak de klassen isoleren tijdens het testen.
Dependency injectie is een praktijk waarbij objecten worden ontworpen op een manier waarbij zij instanties van de objecten ontvangen van andere stukken code, in plaats van ze intern te bouwen. Dit betekent dat om het even welk object dat de interface implementeert die door het object wordt vereist, kan worden gesubstitueerd zonder de code te veranderen, wat het testen vereenvoudigt, en de ontkoppeling verbetert.
Neem bijvoorbeeld deze clases:
public class PersonService {
public void addManager( Person employee, Person newManager ) { ... }
public void removeManager( Person employee, Person oldManager ) { ... }
public Group getGroupByManager( Person manager ) { ... }
}
public class GroupMembershipService() {
public void addPersonToGroup( Person person, Group group ) { ... }
public void removePersonFromGroup( Person person, Group group ) { ... }
}
In dit voorbeeld zou de implementatie van PersonService::addManager
en PersonService::removeManager
een instantie van de GroupMembershipService
nodig hebben om zijn werk te kunnen doen. Zonder Dependency Injection, zou de traditionele manier om dit te doen zijn om een nieuwe GroupMembershipService
te instantiëren in de constructor van PersonService
en die instantie attribuut te gebruiken in beide functies. Echter, als de constructor van GroupMembershipService
meerdere dingen heeft die het nodig heeft, of erger nog, er zijn een aantal initialisatie "setters" die moeten worden aangeroepen op de GroupMembershipService
, dan groeit de code nogal snel, en de PersonService
is nu niet alleen afhankelijk van de GroupMembershipService
maar ook van al het andere waar GroupMembershipService
van afhankelijk is. Bovendien is de koppeling met GroupMembershipService
hard gecodeerd in de PersonService
, wat betekent dat je geen GroupMembershipService
kunt "dummy up" voor test doeleinden, of om een strategie patroon te gebruiken in verschillende delen van je applicatie.
Met Dependency Injection, in plaats van het instantiëren van de GroupMembershipService
binnen uw PersonService
, zou u het ofwel doorgeven aan de PersonService
constructor, of anders een Property toevoegen (getter en setter) om een lokale instantie ervan in te stellen. Dit betekent dat uw PersonService
zich niet langer zorgen hoeft te maken over hoe een GroupMembershipService
aan te maken, het accepteert gewoon degene die het krijgt, en werkt ermee. Dit betekent ook dat alles wat een subklasse is van GroupMembershipService
, of de GroupMembershipService
interface implementeert, kan worden "injected" in de PersonService
, en de PersonService
hoeft niet te weten van de verandering.