Wat is afhankelijkheidsinjectie?

Er zijn al verschillende vragen gepost met specifieke vragen over afhankelijkheidsinjectie, zoals wanneer gebruiken en welke kaders er voor zijn. Echter,

Wat is afhankelijkheidsinjectie en wanneer/waarom moet het wel of niet worden gebruikt?


Antwoord 1, autoriteit 100%

Dependency Injection geeft afhankelijkheid door aan andere objecten of framework( dependency-injector).

Injectie met afhankelijkheid maakt testen eenvoudiger. De injectie kan worden gedaan via constructor.

SomeClass() heeft de volgende constructor:

public SomeClass() {
    myObject = Factory.getObject();
}

Probleem:
Als myObject complexe taken omvat, zoals schijftoegang of netwerktoegang, is het moeilijk om unit-tests uit te voeren op SomeClass(). Programmeurs moeten myObject bespotten en kunnen de fabrieksoproep onderscheppen.

Alternatieve oplossing:

  • myObject doorgeven als argument aan de constructor
public SomeClass (MyClass myObject) {
    this.myObject = myObject;
}

myObject kan direct worden doorgegeven, wat het testen eenvoudiger maakt.

  • Een veelgebruikt alternatief is het definiëren van een nietsdoen-constructor. Afhankelijkheidsinjectie kan worden gedaan via setters. (h/t @MikeVella).
  • Martin Fowler documenteert een derde alternatief (h/t @MarcDix), waarbij klassen implementeren expliciet een interface voor de afhankelijkheden die programmeurs willen geïnjecteerd.

Het is moeilijker om componenten te isoleren in unit-tests zonder afhankelijkheidsinjectie.

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 servicelocator of soortgelijke patronen). Programmeurs moeten de klassen vaak isoleren tijdens het testen.


Antwoord 2, autoriteit 92%

De beste definitie die ik tot nu toe heb gevonden is een door James Shore :

‘Dependency Injection’ kost 25 dollar
term voor een concept van 5 cent. […]
Afhankelijkheidsinjectie betekent het geven van een
object zijn instantievariabelen. […].

Er is een artikel van Martin Fowler dat ook nuttig kan zijn.

Injectie met afhankelijkheid is in feite het leveren van de objecten die een object nodig heeft (zijn afhankelijkheden) in plaats van ze zelf te laten construeren. Het is een zeer nuttige techniek om te testen, omdat hiermee afhankelijkheden kunnen worden bespot of uitgebannen.

Afhankelijkheden kunnen op vele manieren in objecten worden geïnjecteerd (zoals constructorinjectie of setterinjectie). Je kunt daarvoor zelfs gespecialiseerde afhankelijkheidsinjectie-frameworks (bijvoorbeeld Spring) gebruiken, maar dat is zeker niet vereist. U hebt die frameworks niet nodig om afhankelijkheidsinjectie te hebben. Het expliciet instantiëren en doorgeven van objecten (afhankelijkheden) is net zo’n goede injectie als injectie per framework.


Antwoord 3, autoriteit 35%

Ik vond dit grappige voorbeeld in termen van losse koppeling:

Bron: Injectie met afhankelijkheid begrijpen

Elke toepassing bestaat uit vele objecten die met elkaar samenwerken om nuttige dingen uit te voeren. Traditioneel is elk object verantwoordelijk voor het verkrijgen van zijn eigen verwijzingen naar de afhankelijke objecten (afhankelijkheden) waarmee het samenwerkt. Dit leidt tot sterk gekoppelde klassen en moeilijk te testen code.

Beschouw bijvoorbeeld een Car object.

Een Car is afhankelijk van wielen, motor, brandstof, accu, enz. om te kunnen rijden. Traditioneel definiëren we het merk van dergelijke afhankelijke objecten samen met de definitie van het Car-object.

Zonder Dependency Injection (DI):

class Car{
  private Wheel wh = new NepaliRubberWheel();
  private Battery bt = new ExcideBattery();
  //The rest
}

Hier is het Car object verantwoordelijk voor het maken van de afhankelijke objecten.

Wat als we het type van zijn afhankelijke object – zeg Wheel – willen veranderen na de eerste NepaliRubberWheel() lekke banden?
We moeten het Car-object opnieuw maken met zijn nieuwe afhankelijkheid, bijvoorbeeld ChineseRubberWheel(), maar alleen de fabrikant van Car kan dat doen.

Wat doet de Dependency Injection dan voor ons…?

Bij gebruik van afhankelijkheidsinjectie krijgen objecten hun afhankelijkheden tijdens runtime in plaats van compileertijd (autoproductietijd).
Zodat we nu het Wheel kunnen veranderen wanneer we maar willen. Hier kan de dependency (Wheel) tijdens runtime in Car worden geïnjecteerd.

Na gebruik van afhankelijkheidsinjectie:

Hier injecteren we de afhankelijkheden (wiel en batterij) tijdens runtime. Vandaar de term: Dependency Injection. We vertrouwen normaal gesproken op DI-frameworks zoals Spring, Guice, Weld om de afhankelijkheden te creëren en te injecteren waar nodig.

class Car{
  private Wheel wh; // Inject an Instance of Wheel (dependency of car) at runtime
  private Battery bt; // Inject an Instance of Battery (dependency of car) at runtime
  Car(Wheel wh,Battery bt) {
      this.wh = wh;
      this.bt = bt;
  }
  //Or we can have setters
  void setWheel(Wheel wh) {
      this.wh = wh;
  }
}

De voordelen zijn:

  • het maken van een object loskoppelen (met andere woorden, het gebruik scheiden van het maken van een object)
  • mogelijkheid om afhankelijkheden te vervangen (bijvoorbeeld: wiel, batterij) zonder de klasse te veranderen die het gebruikt (auto)
  • bevordert het principe “Code naar interface, niet naar implementatie”
  • mogelijkheid om namaakafhankelijkheid te creëren en te gebruiken tijdens de test (als we een Mock of Wheel willen gebruiken tijdens de test in plaats van een echte instantie.. we kunnen een Mock Wheel-object maken en DI-framework in Car laten injecteren)

Antwoord 4, autoriteit 14%

Dependency Injection is een praktijk waarbij objecten zo worden ontworpen dat ze instanties van de objecten uit andere stukjes code ontvangen, in plaats van ze intern te construeren. Dit betekent dat elk object dat de interface implementeert die door het object wordt vereist, kan worden vervangen zonder de code te wijzigen, wat het testen vereenvoudigt en de ontkoppeling verbetert.

Beschouw bijvoorbeeld deze klassen:

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 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 dat instantiekenmerk in beide functies te gebruiken. Als de constructor van GroupMembershipService echter meerdere dingen heeft die hij nodig heeft, of erger nog, er zijn enkele initialisatie-“setters” die moeten worden aangeroepen op de GroupMembershipService, dan groeit de code vrij snel, en de PersonService hangt nu niet alleen af ​​van de GroupMembershipService maar ook van al het andere waarvan GroupMembershipService afhankelijk is. Bovendien is de koppeling met GroupMembershipService hard gecodeerd in de PersonService, wat betekent dat u een GroupMembershipService niet kunt “dummy-uppen” voor testdoeleinden, of om een ​​strategiepatroon in verschillende delen van uw applicatie te gebruiken.

Met Dependency Injection, in plaats van de GroupMembershipService te instantiëren binnen uw PersonService, zou u deze ofwel doorgeven aan de PersonService constructor, of voeg anders een eigenschap toe (getter en setter) om er een lokale instantie van in te stellen. Dit betekent dat uw PersonService zich geen zorgen meer hoeft te maken over het aanmaken van een GroupMembershipService, het accepteert alleen de aangeboden services en werkt ermee samen. Dit betekent ook dat alles wat een subklasse is van GroupMembershipService, of de GroupMembershipService-interface implementeert, kan worden “geïnjecteerd” in de PersonService, en de PersonService hoeft niets van de wijziging te weten.


Antwoord 5, autoriteit 9%

Het geaccepteerde antwoord is goed – maar ik wil hieraan toevoegen dat DI veel lijkt op het klassieke vermijden van hardgecodeerde constanten in de code.

Als je een constante gebruikt, zoals een databasenaam, zou je deze snel van de binnenkant van de code naar een configuratiebestand verplaatsen en een variabele met die waarde doorgeven aan de plaats waar deze nodig is. De reden om dat te doen is dat deze constanten meestal vaker veranderen dan de rest van de code. Bijvoorbeeld als u de code in een testdatabase wilt testen.

DI is analoog aan dit in de wereld van Object Oriented Programming. De waarden daar in plaats van constante letterlijke waarden zijn hele objecten – maar de reden om de code te verplaatsen om ze uit de klassencode te halen is vergelijkbaar – de objecten veranderen vaker dan de code die ze gebruikt. Een belangrijk geval waarin zo’n verandering nodig is, zijn tests.


Antwoord 6, autoriteit 8%

Laten we een eenvoudig voorbeeld proberen met de klassen Auto en Motor, elke auto heeft een motor nodig om ergens heen te gaan, althans voorlopig. Dus hieronder hoe code eruit zal zien zonder afhankelijkheidsinjectie.

public class Car
{
    public Car()
    {
        GasEngine engine = new GasEngine();
        engine.Start();
    }
}
public class GasEngine
{
    public void Start()
    {
        Console.WriteLine("I use gas as my fuel!");
    }
}

En om de autoklasse te instantiëren, gebruiken we de volgende code:

Car car = new Car();

Het probleem met deze code die we nauw hebben gekoppeld aan GasEngine en als we besluiten deze te wijzigen in ElectricityEngine, moeten we de autoklasse herschrijven. En hoe groter de applicatie, hoe meer problemen en hoofdpijn we zullen hebben om een ​​nieuw type motor toe te voegen en te gebruiken.

Met andere woorden, met deze benadering is onze auto-klasse op hoog niveau afhankelijk van de lagere GasEngine-klasse die het Dependency Inversion Principle (DIP) van SOLID schenden. DIP suggereert dat we moeten vertrouwen op abstracties, niet op concrete klassen. Om hieraan te voldoen introduceren we de IEngine-interface en herschrijven we de code zoals hieronder:

    public interface IEngine
    {
        void Start();
    }
    public class GasEngine : IEngine
    {
        public void Start()
        {
            Console.WriteLine("I use gas as my fuel!");
        }
    }
    public class ElectricityEngine : IEngine
    {
        public void Start()
        {
            Console.WriteLine("I am electrocar");
        }
    }
    public class Car
    {
        private readonly IEngine _engine;
        public Car(IEngine engine)
        {
            _engine = engine;
        }
        public void Run()
        {
            _engine.Start();
        }
    }

Onze autoklasse is nu alleen afhankelijk van de IEngine-interface, niet van een specifieke implementatie van de motor.
De enige truc is nu hoe we een instantie van de auto kunnen maken en deze een echte concrete motorklasse kunnen geven, zoals GasEngine of ElectricityEngine. Dat is waar Dependency Injection van pas komt.

   Car gasCar = new Car(new GasEngine());
   gasCar.Run();
   Car electroCar = new Car(new ElectricityEngine());
   electroCar.Run();

Hier injecteren (passeren) we onze afhankelijkheid (instantie van de motor) in de autoconstructor. Onze klassen hebben nu dus een losse koppeling tussen objecten en hun afhankelijkheden, en we kunnen eenvoudig nieuwe typen motoren toevoegen zonder de klasse Car te wijzigen.

Het belangrijkste voordeel van de Dependency Injection is dat klassen losser gekoppeld zijn, omdat ze geen hardgecodeerde afhankelijkheden hebben. Dit volgt het Dependency Inversion Principle, dat hierboven werd genoemd. In plaats van te verwijzen naar specifieke implementaties, vragen klassen om abstracties (meestal interfaces) die aan hen worden verstrekt wanneer de klasse wordt geconstrueerd.

Dus uiteindelijk is Injectie met afhankelijkheid slechts een techniek voor
het bereiken van losse koppeling tussen objecten en hun afhankelijkheden.
In plaats van direct afhankelijkheden te instantiëren die de klasse nodig heeft,
om zijn acties uit te voeren, worden afhankelijkheden aan de klasse verstrekt
(meestal) via constructorinjectie.

Ook als we veel afhankelijkheden hebben, is het een goede gewoonte om Inversion of Control (IoC) -containers te gebruiken, zodat we kunnen zien welke interfaces moeten worden toegewezen aan welke concrete implementaties voor al onze afhankelijkheden en we kunnen deze afhankelijkheden voor ons laten oplossen wanneer het ons object construeert. We zouden bijvoorbeeld in de toewijzing voor de IoC-container kunnen specificeren dat de IEngine-afhankelijkheid moet worden toegewezen aan de klasse GasEngine en wanneer we de IoC-container vragen om een ​​exemplaar van onze Auto klasse, het zal automatisch onze Auto klasse samenstellen met een GasEngine afhankelijkheid doorgegeven.

UPDATE: Ik heb onlangs een cursus over EF Core van Julie Lerman bekeken en vond haar korte definitie over DI ook goed.

Injectie met afhankelijkheid is een patroon waarmee uw toepassing kan injecteren
objecten on-the-fly naar klassen die ze nodig hebben, zonder die te forceren
klassen die verantwoordelijk zijn voor die objecten. Hiermee kan uw code worden
losser gekoppeld, en Entity Framework Core sluit hierop aan
systeem van diensten.


Antwoord 7, autoriteit 6%

Stel je voor dat je wilt gaan vissen:

  • Zonder afhankelijkheidsinjectie moet u alles zelf regelen. Je moet een boot vinden, een hengel kopen, aas zoeken, enz. Het is natuurlijk mogelijk, maar het legt veel verantwoordelijkheid bij je. In softwaretermen betekent dit dat je al deze dingen moet opzoeken.

  • Bij afhankelijkheidsinjectie zorgt iemand anders voor alle voorbereiding en stelt hij de benodigde apparatuur voor u ter beschikking. U ontvangt (“wordt geïnjecteerd”) de boot, de hengel en het aas – allemaal klaar voor gebruik.


Antwoord 8, autoriteit 5%

Dit is de meest eenvoudige uitleg over Dependency Injection en Afhankelijkheidsinjectiecontainer die ik ooit heb gezien:

Zonder afhankelijkheidsinjectie

  • Applicatie heeft Foo nodig (bijvoorbeeld een controller), dus:
  • Applicatie maakt Foo
  • Applicatie roept Foo . op
    • Foo heeft Bar nodig (bijvoorbeeld een dienst), dus:
    • Foo maakt Bar
    • Foo roept Bar
      • Bar heeft Bim nodig (een service, een repository,
        ), dus:
      • Bar maakt Bim
      • Bar doet iets

Met afhankelijkheidsinjectie

  • Applicatie heeft Foo nodig, die heeft Bar nodig, die heeft Bim nodig, dus:
  • Applicatie maakt Bim
  • Applicatie maakt Bar en geeft het een Bim
  • Applicatie maakt Foo en geeft het Bar
  • Applicatie roept Foo . op
    • Foo roept Bar
      • Bar doet iets

Een afhankelijkheidsinjectiecontainer gebruiken

  • Applicatie heeft Foo nodig, dus:
  • Applicatie haalt Foo uit de container, dus:
    • Container maakt BIM
    • Container maakt Bar en geeft het een Bim
    • Container maakt Foo en geeft het Bar
  • Applicatie roept Foo . op
    • Foo roept Bar
      • Bar doet iets

Dependency Injection en dependency Injection Containers zijn verschillende dingen:

  • Dependency Injection is een methode om betere code te schrijven
  • een DI-container is een hulpmiddel om afhankelijkheden te injecteren

U hebt geen container nodig om afhankelijkheidsinjectie te doen. Een container kan u echter helpen.


Antwoord 9, autoriteit 3%

Betekent ‘injectie van afhankelijkheid’ niet alleen het gebruik van geparametriseerde constructors en openbare setters?

Het artikel van James Shore toont de volgende voorbeelden ter vergelijking.

Constructor zonder afhankelijkheidsinjectie:

public class Example { 
  private DatabaseThingie myDatabase; 
  public Example() { 
    myDatabase = new DatabaseThingie(); 
  } 
  public void doStuff() { 
    ... 
    myDatabase.getData(); 
    ... 
  } 
} 

Constructor met afhankelijkheidsinjectie:

public class Example { 
  private DatabaseThingie myDatabase; 
  public Example(DatabaseThingie useThisDatabaseInstead) { 
    myDatabase = useThisDatabaseInstead; 
  }
  public void doStuff() { 
    ... 
    myDatabase.getData(); 
    ... 
  } 
}

Antwoord 10, autoriteit 2%

Om het concept van afhankelijkheidsinjectie eenvoudig te begrijpen te maken. Laten we een voorbeeld nemen van een schakelknop om een ​​lamp in/uit te schakelen.

Zonder afhankelijkheidsinjectie

Switch moet van tevoren weten met welke lamp ik ben verbonden (hardgecodeerde afhankelijkheid). Dus,

Schakelaar -> PermanentBulb //switch is direct aangesloten op permanente lamp, testen niet gemakkelijk mogelijk

Switch(){
PermanentBulb = new Bulb();
PermanentBulb.Toggle();
}

Met afhankelijkheidsinjectie

Switch weet alleen dat ik de lamp moet in- of uitschakelen die aan mij wordt doorgegeven. Dus,

Schakelaar -> Bulb1 OR Bulb2 OR NightBulb (geïnjecteerde afhankelijkheid)

Switch(AnyBulb){ //pass it whichever bulb you like
AnyBulb.Toggle();
}

Aanpassen James Voorbeeld voor Switch en Bulb:

public class SwitchTest { 
  TestToggleBulb() { 
    MockBulb mockbulb = new MockBulb(); 
    // MockBulb is a subclass of Bulb, so we can 
    // "inject" it here: 
    Switch switch = new Switch(mockBulb); 
    switch.ToggleBulb(); 
    mockBulb.AssertToggleWasCalled(); 
  } 
}
public class Switch { 
  private Bulb myBulb; 
  public Switch() { 
    myBulb = new Bulb(); 
  } 
  public Switch(Bulb useThisBulbInstead) { 
    myBulb = useThisBulbInstead; 
  } 
  public void ToggleBulb() { 
    ... 
    myBulb.Toggle(); 
    ... 
  } 
}`

Antwoord 11, autoriteit 2%

Voordat je naar de technische beschrijving gaat, visualiseer het eerst met een realistisch voorbeeld, want je zult veel technische dingen vinden om afhankelijkheidsinjectie te leren, maar de meerderheid van de mensen kan het kernconcept ervan niet begrijpen.

Veronderstel in de eerste afbeelding dat je een autofabriek hebt met veel eenheden. Een auto is eigenlijk gebouwd in de montage-eenheid, maar deze heeft motor, stoelen en wielen nodig. Dus een assemblage-eenheid is afhankelijk van deze alle eenheden en ze zijn de afhankelijkheden van de fabriek.

Je voelt dat het nu te ingewikkeld is om alle taken in deze fabriek te onderhouden, want naast de hoofdtaak (een auto in elkaar zetten in de montage-eenheid) moet je je ook concentreren op andere eenheden. Het is nu erg duur om te onderhouden en het fabrieksgebouw is enorm, dus het kost je extra geld om te huren.

Kijk nu naar de tweede foto. Als u een aantal leveranciers vindt die u het wiel, stoel en motor leveren voor goedkoper dan uw eigen productiekosten, dan kunt u nu u hoeft ze niet in uw fabriek te maken. U kunt nu een kleiner gebouw huren alleen voor uw montage-eenheid, wat uw onderhoudstaak en uw extra huurkosten zal verminderen. Nu kunt u zich ook alleen concentreren op uw hoofdtaak (automontage).

Nu kunnen we zeggen dat alle afhankelijkheden voor het assembleren van een auto in de fabriek worden gespoten door de leveranciers. Het is een voorbeeld van een real-life Dependency Injection (DI).

In het technische woord is afhankelijkheidsinjectie een techniek waarbij een object (of statische methode) de afhankelijkheden van een ander object levert. Dus het overdragen van de taak van het maken van het object aan iemand anders en het direct gebruiken van de afhankelijkheid wordt afhankelijkheidsinjectie genoemd.

Dit zal je nu helpen om DI te leren met een technische uitleg. Dit laat zien wanneer DI moet worden gebruikt en wanneer je moet niet.

Alles in één autofabriek.

Eenvoudige autofabriek


Antwoord 12, autoriteit 2%

Wat is Dependency Injection (DI)?

Zoals anderen al hebben gezegd, verwijdert Dependency Injection(DI) de verantwoordelijkheid voor het direct creëren en beheren van de levensduur van andere objectinstanties waarvan onze interesseklasse (consumentenklasse) afhankelijk is ( in de UML-zin). Deze instanties worden in plaats daarvan doorgegeven aan onze consumentenklasse, meestal als constructorparameters of via eigenschapsinstellingen (het beheer van het afhankelijkheidsobject dat wordt geïnstantieerd en doorgegeven aan de consumentenklasse wordt meestal uitgevoerd door een Inversion of Control (IoC) container, maar dat is een ander onderwerp).

DI, DIP en SOLID

Met name in het paradigma van Robert C Martin’s SOLID-principes van objectgeoriënteerd ontwerp, DI is een van de mogelijke implementaties van het Dependency Inversion Principle (DIP) . De DIP is de D van de SOLID mantra – andere DIP implementaties omvatten de Service Locator en Plugin-patronen.

Het doel van de DIP is om strakke, concrete afhankelijkheden tussen klassen te ontkoppelen en in plaats daarvan de koppeling los te maken door middel van een abstractie, die kan worden bereikt via een interface, abstract class of pure virtual class, afhankelijk van de gebruikte taal en aanpak.

Zonder de DIP is onze code (ik heb deze ‘consumerende klasse’ genoemd) direct gekoppeld aan een concrete afhankelijkheid en is ook vaak belast met de verantwoordelijkheid om te weten hoe een instantie van deze afhankelijkheid te verkrijgen en te beheren, dwz conceptueel:

"I need to create/use a Foo and invoke method `GetBar()`"

Terwijl na toepassing van de DIP de vereiste is versoepeld en de zorg om de levensduur van de Foo-afhankelijkheid te verkrijgen en te beheren is weggenomen:

"I need to invoke something which offers `GetBar()`"

Waarom DIP (en DI) gebruiken?

Het op deze manier ontkoppelen van afhankelijkheden tussen klassen zorgt voor gemakkelijke vervanging van deze afhankelijkheidsklassen met andere implementaties die ook voldoen aan de vereisten van de abstractie (de afhankelijkheid kan bijvoorbeeld worden geschakeld met een andere implementatie van dezelfde interface ). Bovendien, zoals anderen al hebben gezegd, is mogelijk de meest voorkomende reden om klassen te ontkoppelen via de DIP om een ​​consumerende klasse afzonderlijk te testen, aangezien dezelfde afhankelijkheden nu kunnen worden afgekeurd en/of bespot.

Een gevolg van DI is dat het beheer van de levensduur van instanties van afhankelijkheidsobjecten niet langer wordt gecontroleerd door een consumerende klasse, omdat het afhankelijkheidsobject nu wordt doorgegeven aan de consumerende klasse (via constructor- of setter-injectie).

Dit kan op verschillende manieren worden bekeken:

  • Als de levensduurcontrole van afhankelijkheden door de consumerende klasse moet worden behouden, kan de controle worden hersteld door een (abstracte) fabriek te injecteren voor het maken van de afhankelijkheidsklasse-instanties in de consumentenklasse. De consument kan indien nodig instances verkrijgen via een Create in de fabriek, en deze instances verwijderen zodra deze zijn voltooid.
  • Of de levensduurcontrole van afhankelijkheidsinstanties kan worden overgelaten aan een IoC-container (meer hierover hieronder).

Wanneer DI gebruiken?

  • Waar het waarschijnlijk nodig zal zijn om een ​​afhankelijkheid te vervangen door een gelijkwaardige implementatie,
  • Elke keer dat u de methoden van een klasse afzonderlijk van zijn afhankelijkheden moet testen,
  • Waar onzekerheid over de levensduur van een afhankelijkheid experimenten kan rechtvaardigen (bijv. Hey, MyDepClass is thread-safe – wat als we er een singleton van maken en dezelfde instantie in alle consumenten injecteren?)

Voorbeeld

Hier is een eenvoudige C#-implementatie. Gezien de onderstaande verbruiksklasse:

public class MyLogger
{
   public void LogRecord(string somethingToLog)
   {
      Console.WriteLine("{0:HH:mm:ss} - {1}", DateTime.Now, somethingToLog);
   }
}

Hoewel het schijnbaar onschuldig is, heeft het twee static afhankelijkheden van twee andere klassen, System.DateTime en System.Console, die niet alleen de de uitvoeropties voor loggen (loggen op console is waardeloos als niemand kijkt), maar erger nog, het is moeilijk om automatisch te testen gezien de afhankelijkheid van een niet-deterministische systeemklok.

We kunnen echter DIP toepassen op deze klasse, door de zorg van tijdstempels als afhankelijkheid weg te nemen, en MyLogger alleen te koppelen aan een eenvoudige interface:

public interface IClock
{
    DateTime Now { get; }
}

We kunnen de afhankelijkheid van Console ook losmaken tot een abstractie, zoals een TextWriter. Afhankelijkheidsinjectie wordt meestal geïmplementeerd als constructor-injectie (een abstractie doorgeven aan een afhankelijkheid als een parameter aan de constructor van een consumerende klasse) of Setter Injection (de afhankelijkheid doorgeven via een setXyz() setter of een .Net Property met {set;} gedefinieerd). Constructor Injection heeft de voorkeur, omdat dit garandeert dat de klasse na de constructie in de juiste staat zal zijn en de interne afhankelijkheidsvelden kunnen worden gemarkeerd als readonly (C#) of final ( Java). Dus als we constructorinjectie gebruiken in het bovenstaande voorbeeld, blijven we over met:

public class MyLogger : ILogger // Others will depend on our logger.
{
    private readonly TextWriter _output;
    private readonly IClock _clock;
    // Dependencies are injected through the constructor
    public MyLogger(TextWriter stream, IClock clock)
    {
        _output = stream;
        _clock = clock;
    }
    public void LogRecord(string somethingToLog)
    {
        // We can now use our dependencies through the abstraction 
        // and without knowledge of the lifespans of the dependencies
        _output.Write("{0:yyyy-MM-dd HH:mm:ss} - {1}", _clock.Now, somethingToLog);
    }
}

(Er moet een concrete Clock worden opgegeven, die natuurlijk kan terugkeren naar DateTime.Now, en de twee afhankelijkheden moeten worden geleverd door een IoC-container via een constructor injectie)

Er kan een geautomatiseerde Unit Test worden gebouwd, die definitief bewijst dat onze logger correct werkt, omdat we nu controle hebben over de afhankelijkheden – de tijd, en we kunnen de geschreven uitvoer bespioneren:

[Test]
public void LoggingMustRecordAllInformationAndStampTheTime()
{
    // Arrange
    var mockClock = new Mock<IClock>();
    mockClock.Setup(c => c.Now).Returns(new DateTime(2015, 4, 11, 12, 31, 45));
    var fakeConsole = new StringWriter();
    // Act
    new MyLogger(fakeConsole, mockClock.Object)
        .LogRecord("Foo");
    // Assert
    Assert.AreEqual("2015-04-11 12:31:45 - Foo", fakeConsole.ToString());
}

Volgende stappen

Injectie met afhankelijkheid wordt altijd geassocieerd met een Inversion of Control container (IoC), om ( bieden) de concrete afhankelijkheidsinstanties en om levensduurinstanties te beheren. Tijdens het configuratie-/bootstrapping-proces kunnen IoC-containers het volgende definiëren:

  • mapping tussen elke abstractie en de geconfigureerde concrete implementatie (bijv. “elke keer dat een consument een IBar aanvraagt, een ConcreteBar-instantie retourneren”)
  • beleid kan worden ingesteld voor het beheer van de levensduur van elke afhankelijkheid, b.v. om een ​​nieuw object te maken voor elke consumenteninstantie, om een ​​singleton-afhankelijkheidsinstantie te delen met alle consumenten, om dezelfde afhankelijkheidsinstantie alleen over dezelfde thread te delen, enz.
  • In .Net zijn IoC-containers op de hoogte van protocollen zoals IDisposable en nemen ze de verantwoordelijkheid op zich voor Disposing afhankelijkheden in overeenstemming met het geconfigureerde levensduurbeheer.
  • >

Als IoC-containers eenmaal geconfigureerd/bootstrapt zijn, werken ze normaal gesproken naadloos op de achtergrond, zodat de codeur zich kan concentreren op de code die voorhanden is in plaats van zich zorgen te maken over afhankelijkheden.

De sleutel tot DI-vriendelijke code is om statische koppeling van klassen te vermijden, en niet om new() te gebruiken voor het maken van afhankelijkheden

Zoals in het bovenstaande voorbeeld vereist het ontkoppelen van afhankelijkheden enige ontwerpinspanning, en voor de ontwikkelaar is er een paradigmaverschuiving nodig om de gewoonte van newafhankelijkheden direct te doorbreken en in plaats daarvan te vertrouwen op de container afhankelijkheden te beheren.

Maar de voordelen zijn talrijk, vooral in de mogelijkheid om uw interesseklasse grondig te testen.

Opmerking: de creatie / mapping / projectie (via new ..()) van POCO / POJO / Serialisatie DTO’s / Entity Graphs / Anonieme JSON-projecties et al – dwz “Alleen gegevens” klassen of records – gebruikt of geretourneerd door methoden worden niet beschouwd als afhankelijkheden (in de UML-zin) en niet onderworpen aan DI. Het is prima om new te gebruiken om deze te projecteren.


Antwoord 13

Het hele punt van Dependency Injection (DI) is om de broncode van de applicatie schoon en stabiel te houden:

  • schoon van afhankelijkheidsinitialisatiecode
  • stabiel ongeacht de gebruikte afhankelijkheid

Praktisch gezien scheidt elk ontwerppatroon zorgen om toekomstige wijzigingen van invloed te laten zijn op minimale bestanden.

Het specifieke domein van DI is delegatie van afhankelijkheidsconfiguratie en initialisatie.

Voorbeeld: DI met shellscript

Als je af en toe buiten Java werkt, herinner je dan hoe source vaak wordt gebruikt in veel scripttalen (Shell, Tcl, enz., of zelfs import in Python die wordt misbruikt voor dit doel).

Overweeg een eenvoudig dependent.sh-script:

#!/bin/sh
# Dependent
touch         "one.txt" "two.txt"
archive_files "one.txt" "two.txt"

Het script is afhankelijk: het wordt niet zelfstandig uitgevoerd (archive_files is niet gedefinieerd).

U definieert archive_files in het archive_files_zip.sh implementatiescript (gebruik in dit geval zip):

#!/bin/sh
# Dependency
function archive_files {
    zip files.zip "$@"
}

In plaats van source-ing implementatiescript rechtstreeks in de afhankelijke, gebruikt u een injector.sh “container” die beide “componenten” omhult:

#!/bin/sh 
# Injector
source ./archive_files_zip.sh
source ./dependent.sh

De archive_files afhankelijkheid is zojuist geïnjecteerd in het afhankelijke script.

Je zou afhankelijkheid kunnen hebben geïnjecteerd die archive_files implementeert met behulp van tar of xz.

Voorbeeld: DI verwijderen

Als het dependent.sh-script afhankelijkheden rechtstreeks zou gebruiken, zou de aanpak dependency lookup heten (wat het tegenovergestelde is van dependency injection):

#!/bin/sh
# Dependent
# dependency look-up
source ./archive_files_zip.sh
touch         "one.txt" "two.txt"
archive_files "one.txt" "two.txt"

Het probleem is nu dat de afhankelijke “component” de initialisatie zelf moet uitvoeren.

De broncode van de “component” is niet schoon noch stabiel omdat elke wijziging in de initialisatie van afhankelijkheden een nieuwe release vereist voor het broncodebestand van “componenten”, zoals goed.

Laatste woorden

DI wordt niet zo sterk benadrukt en populair gemaakt als in Java-frameworks.

Maar het is een algemene benadering om de zorgen te splitsen van:

  • applicatie ontwikkeling (enkele levenscyclus van broncode-release)
  • toepassing implementatie (meerdere doelomgevingen met onafhankelijke levenscycli)

Alleen configuratie gebruiken met dependency lookup helpt niet, aangezien het aantal configuratieparameters per afhankelijkheid kan veranderen (bijv. nieuw authenticatietype) evenals het aantal ondersteunde typen afhankelijkheden (bijv. nieuw databasetype).


Antwoord 14

Alle bovenstaande antwoorden zijn goed, mijn doel is om het concept op een eenvoudige manier uit te leggen, zodat iedereen zonder programmeerkennis het concept ook kan begrijpen

Dependency Injection is een van de ontwerppatronen die ons helpen om complexe systemen op een eenvoudigere manier te creëren.

We zien een grote verscheidenheid aan toepassingen van dit patroon in ons dagelijks leven.
Enkele voorbeelden zijn Bandrecorder, VCD, CD Drive enz.

Reel-to-reel draagbare bandrecorder, midden 20e eeuw.

De bovenstaande afbeelding is een afbeelding van een Reel-to-Reel draagbare bandrecorder, midden 20e eeuw. Bron.

De primaire bedoeling van een bandrecorder is om geluid op te nemen of af te spelen.

Bij het ontwerpen van een systeem is een haspel nodig om geluid of muziek op te nemen of af te spelen. Er zijn twee mogelijkheden om dit systeem te ontwerpen

  1. we kunnen de haspel in de machine plaatsen
  2. we kunnen een haak voor de haspel leveren waar deze kan worden geplaatst.

Als we de eerste gebruiken, moeten we de machine openen om de haspel te wisselen.
als we kiezen voor de tweede, dat wil zeggen het plaatsen van een haak voor haspel, krijgen we een bijkomend voordeel van het spelen van muziek door de haspel te veranderen. en ook de functie terugbrengen tot het spelen van wat er op de rol staat.

Net als bij wijze van afhankelijkheid is injectie het proces van het externaliseren van de afhankelijkheden om alleen te focussen op de specifieke functionaliteit van de component, zodat onafhankelijke componenten aan elkaar kunnen worden gekoppeld om een ​​complex systeem te vormen.

De belangrijkste voordelen die we hebben bereikt door afhankelijkheidsinjectie te gebruiken.

  • Hoge cohesie en losse koppeling.
  • Afhankelijkheid naar buiten brengen en alleen kijken naar verantwoordelijkheid.
  • Dingen maken als componenten en combineren om een ​​groot systeem met hoge mogelijkheden te vormen.
  • Het helpt om componenten van hoge kwaliteit te ontwikkelen, omdat ze onafhankelijk zijn ontwikkeld en goed worden getest.
  • Het helpt om het onderdeel te vervangen door een ander als er een faalt.

Tegenwoordig vormen deze concepten de basis van bekende frameworks in de programmeerwereld.
De Spring Angular etc zijn de bekende software frameworks die bovenop dit concept zijn gebouwd

Afhankelijkheidsinjectie is een patroon dat wordt gebruikt om instanties te maken van objecten waarop andere objecten vertrouwen, zonder dat u tijdens het compileren weet welke klasse zal worden gebruikt om die functionaliteit te bieden, of simpelweg de manier waarop eigenschappen aan een object worden toegevoegd, wordt afhankelijkheidsinjectie genoemd.

p>

Voorbeeld voor afhankelijkheidsinjectie

Vroeger schrijven we code zoals deze

Public MyClass{
 DependentClass dependentObject
 /*
  At somewhere in our code we need to instantiate 
  the object with new operator  inorder to use it or perform some method.
  */ 
  dependentObject= new DependentClass();
  dependentObject.someMethod();
}

Met Dependency-injectie neemt de afhankelijkheidsinjector de instantie voor ons uit

Public MyClass{
 /* Dependency injector will instantiate object*/
 DependentClass dependentObject
 /*
  At somewhere in our code we perform some method. 
  The process of  instantiation will be handled by the dependency injector
 */ 
  dependentObject.someMethod();
}

Je kunt ook lezen

Verschil tussen Inversion of Control & Afhankelijkheidsinjectie


Antwoord 15

Voorbeeld, we hebben 2 klassen Client en Service. Client gebruikt Service

public class Service {
    public void doSomeThingInService() {
        // ...
    }
}

Zonder afhankelijkheidsinjectie

Manier 1)

public class Client {
    public void doSomeThingInClient() {
        Service service = new Service();
        service.doSomeThingInService();
    }
}

Manier 2)

public class Client {
    Service service = new Service();
    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

Manier 3)

public class Client {
    Service service;
    public Client() {
        service = new Service();
    }
    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

1) 2) 3) Gebruik

Client client = new Client();
client.doSomeThingInService();

Voordelen

  • Eenvoudig

Nadelen

  • Moeilijk voor test Client klasse
  • Als we de Service-constructor wijzigen, moeten we overal de code wijzigen om een ​​Service-object
  • te maken

Gebruik afhankelijkheidsinjectie

Manier 1) Constructor-injectie

public class Client {
    Service service;
    Client(Service service) {
        this.service = service;
    }
    // Example Client has 2 dependency 
    // Client(Service service, IDatabas database) {
    //    this.service = service;
    //    this.database = database;
    // }
    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

Gebruik

Client client = new Client(new Service());
// Client client = new Client(new Service(), new SqliteDatabase());
client.doSomeThingInClient();

Manier 2) Setter-injectie

public class Client {
    Service service;
    public void setService(Service service) {
        this.service = service;
    }
    public void doSomeThingInClient() {
        service.doSomeThingInService();
    }
}

Gebruik

Client client = new Client();
client.setService(new Service());
client.doSomeThingInClient();

Manier 3) Interface-injectie

Controleer https://en.wikipedia.org/wiki/Dependency_injection

===

Nu volgt deze code al Dependency Injection en is het gemakkelijker voor de test Client-klasse.
We gebruiken echter nog steeds vaak new Service() en het is niet goed om de Service-constructor te wijzigen. Om dit te voorkomen, kunnen we DI-injector gebruiken zoals
1) Eenvoudige handmatige Injector

public class Injector {
    public static Service provideService(){
        return new Service();
    }
    public static IDatabase provideDatatBase(){
        return new SqliteDatabase();
    }
    public static ObjectA provideObjectA(){
        return new ObjectA(provideService(...));
    }
}

Gebruik

Service service = Injector.provideService();

2) Bibliotheek gebruiken: voor Android dagger2

Voordelen

  • Maak de test gemakkelijker
  • Als u de Service wijzigt, hoeft u deze alleen in de Injector-klasse te wijzigen
  • Als u Constructor Injection gebruikt, als u naar de constructor van Client kijkt, ziet u hoeveel afhankelijkheid van de Client-klasse

Nadelen

  • Als u Constructor Injection gebruikt, wordt het Service-object gemaakt wanneer Client wordt gemaakt, soms gebruiken we de functie in Client klasse zonder gebruik Service dus aangemaakte Service is verspild

Definitie van afhankelijkheidsinjectie

https://en.wikipedia.org/wiki/Dependency_injection

Een afhankelijkheid is een object dat kan worden gebruikt (Service)
Een injectie is het doorgeven van een afhankelijkheid (Service) aan een afhankelijk object (Client) dat deze zou gebruiken


Antwoord 16

Wat is afhankelijkheidsinjectie?

Dependency Injection(DI) betekent het ontkoppelen van de objecten die van elkaar afhankelijk zijn. Stel dat object A afhankelijk is van object B, dus het idee is om deze objecten van elkaar los te koppelen. We hoeven het object niet hard te coderen met behulp van een nieuw trefwoord, maar delen afhankelijkheden met objecten tijdens runtime, ondanks de compileertijd.
Als we het hebben over

Hoe Dependency Injection werkt in het voorjaar:

We hoeven het object niet hard te coderen met het nieuwe trefwoord, maar de afhankelijkheid van de bonen in het configuratiebestand te definiëren. De veercontainer is verantwoordelijk voor het aansluiten van alles.

Inversion of Control (IOC)

IOC is een algemeen concept en kan op veel verschillende manieren worden uitgedrukt en Dependency Injection is een concreet voorbeeld van IOC.

Twee soorten afhankelijkheidsinjectie:

  1. Injectie door aannemer
  2. Setter-injectie

1. Op constructor gebaseerde afhankelijkheidsinjectie:

Constructor-gebaseerde DI wordt bereikt wanneer de container een klassenconstructor aanroept met een aantal argumenten, die elk een afhankelijkheid van een andere klasse vertegenwoordigen.

public class Triangle {
private String type;
public String getType(){
    return type;
 }
public Triangle(String type){   //constructor injection
    this.type=type;
 }
}
<bean id=triangle" class ="com.test.dependencyInjection.Triangle">
        <constructor-arg value="20"/>
  </bean>

2. Op setter gebaseerde afhankelijkheidsinjectie:

Setter-gebaseerde DI wordt bereikt door de container die setter-methoden op uw bonen aanroept na het aanroepen van een constructor zonder argumenten of statische fabrieksmethode zonder argumenten om uw bean te instantiëren.

public class Triangle{
 private String type;
 public String getType(){
    return type;
  }
 public void setType(String type){          //setter injection
    this.type = type;
  }
 }
<!-- setter injection -->
 <bean id="triangle" class="com.test.dependencyInjection.Triangle">
        <property name="type" value="equivialteral"/>

OPMERKING:
Het is een goede vuistregel om constructorargumenten te gebruiken voor verplichte afhankelijkheden en setters voor optionele afhankelijkheden. Merk op dat als we annotatie gebruiken die gebaseerd is op @Required annotatie op een setter, kan worden gebruikt om setters als vereiste afhankelijkheden te maken.


Antwoord 17

De beste analogie die ik kan bedenken is de chirurg en zijn assistent(en) in een operatiekamer, waar de chirurg de hoofdpersoon is en zijn assistent die de verschillende chirurgische onderdelen levert wanneer hij die nodig heeft, zodat de chirurg zich kan concentreren aan het ene ding dat hij het beste kan (chirurgie). Zonder assistent moet de chirurg elke keer dat hij er een nodig heeft de onderdelen zelf halen.

DI in het kort, is een techniek om een ​​gemeenschappelijke extra verantwoordelijkheid (last) op componenten weg te nemen om de afhankelijke componenten op te halen, door ze eraan te geven.

DI brengt je dichter bij het Single Responsibility (SR)-principe, zoals de surgeon who can concentrate on surgery.

Wanneer DI gebruiken: ik raad het gebruik van DI aan in bijna alle productieprojecten (klein/groot), vooral in steeds veranderende zakelijke omgevingen 🙂

Waarom: omdat u wilt dat uw code eenvoudig testbaar, bespotbaar enz. is, zodat u uw wijzigingen snel kunt testen en op de markt kunt brengen. Trouwens, waarom zou je dat niet doen als je veel geweldige gratis tools/frameworks hebt om je te ondersteunen op je reis naar een codebase waar je meer controle hebt.


Antwoord 18

Het betekent dat objecten slechts zoveel afhankelijkheden mogen hebben als nodig zijn om hun werk te doen en dat er maar weinig afhankelijkheden zijn. Verder moeten de afhankelijkheden van een object op interfaces liggen en niet op ‘concrete’ objecten, indien mogelijk. (Een concreet object is elk object dat is gemaakt met het trefwoord nieuw.) Losse koppeling bevordert een grotere herbruikbaarheid, gemakkelijker onderhoud en stelt u in staat om gemakkelijk ‘nep’-objecten te leveren in plaats van dure services.

De Dependency Injection (DI) is ook bekend als Inversion of Control (IoC), en kan worden gebruikt als een techniek om deze losse koppeling aan te moedigen.

Er zijn twee primaire benaderingen voor het implementeren van DI:

  1. Constructor injectie
  2. Setter-injectie

Injectie door aannemer

Het is de techniek om afhankelijkheden van objecten door te geven aan de constructor ervan.

Merk op dat de constructor een interface accepteert en geen concreet object. Houd er ook rekening mee dat er een uitzondering wordt gegenereerd als de parameter orderDao null is. Dit benadrukt het belang van het ontvangen van een geldige afhankelijkheid. Constructor Injection is naar mijn mening het voorkeursmechanisme om een ​​object zijn afhankelijkheden te geven. Het is de ontwikkelaar bij het aanroepen van het object duidelijk welke afhankelijkheden aan het Persoon -object moeten worden gegeven voor een juiste uitvoering.

Setter-injectie

Maar kijk eens naar het volgende voorbeeld Stel dat je een klasse hebt met tien methoden die geen afhankelijkheden hebben, maar je voegt een nieuwe methode toe die wel afhankelijk is van IDAO. Je zou de constructor kunnen veranderen om Constructor Injection te gebruiken, maar dit kan je dwingen om alle constructor-aanroepen overal te wijzigen. Als alternatief kunt u gewoon een nieuwe constructor toevoegen die de afhankelijkheid overneemt, maar hoe weet een ontwikkelaar dan gemakkelijk wanneer hij de ene constructor boven de andere moet gebruiken. Ten slotte, als de afhankelijkheid erg duur is om te maken, waarom zou deze dan worden gemaakt en doorgegeven aan de constructor als deze slechts zelden mag worden gebruikt? Setter Injection is een andere DI-techniek die in situaties als deze kan worden gebruikt.

Setter Injection dwingt niet om afhankelijkheden door te geven aan de constructor. In plaats daarvan worden de afhankelijkheden ingesteld op openbare eigenschappen die worden weergegeven door het object in nood. Zoals eerder geïmpliceerd, zijn de belangrijkste drijfveren om dit te doen:

  1. Ondersteuning van afhankelijkheidsinjectie zonder de constructor van een legacy-klasse te hoeven wijzigen.
  2. Laat dure bronnen of services zo laat mogelijk en alleen wanneer nodig worden gemaakt.

Hier is het voorbeeld van hoe de bovenstaande code eruit zou zien:

public class Person {
    public Person() {}
    public IDAO Address {
        set { addressdao = value; }
        get {
            if (addressdao == null)
              throw new MemberAccessException("addressdao" +
                             " has not been initialized");
            return addressdao;
        }
    }
    public Address GetAddress() {
       // ... code that uses the addressdao object
       // to fetch address details from the datasource ...
    }
    // Should not be called directly;
    // use the public property instead
    private IDAO addressdao;

Antwoord 19

Ik weet dat er al veel antwoorden zijn, maar ik vond dit erg nuttig: http: //tutorials.jenkov.com/dependency-injection/index.html

Geen afhankelijkheid:

public class MyDao {
  protected DataSource dataSource = new DataSourceImpl(
    "driver", "url", "user", "password");
  //data access methods...
  public Person readPerson(int primaryKey) {...}     
}

Afhankelijkheid:

public class MyDao {
  protected DataSource dataSource = null;
  public MyDao(String driver, String url, String user, String password) {
    this.dataSource = new DataSourceImpl(driver, url, user, password);
  }
  //data access methods...
  public Person readPerson(int primaryKey) {...}
}

Kijk hoe de DataSourceImpl-instantie naar een constructor wordt verplaatst. De constructor neemt vier parameters die de vier waarden zijn die nodig zijn voor de DataSourceImpl. Hoewel de klasse MyDao nog steeds afhankelijk is van deze vier waarden, voldoet het zelf niet meer aan deze afhankelijkheden. Ze worden geleverd door welke klasse dan ook die een MyDao-instantie maakt.


Antwoord 20

Ik denk dat, aangezien iedereen voor DI heeft geschreven, ik een paar vragen wil stellen..

  1. Als je een DI-configuratie hebt waar alle daadwerkelijke implementaties (geen interfaces) in een klasse worden geïnjecteerd (voor bijvoorbeeld services aan een controller), waarom is dat dan niet een soort van harde codering?
  2. Wat als ik het object tijdens runtime wil wijzigen? Mijn configuratie zegt bijvoorbeeld al wanneer ik MyController start, injecteer voor FileLogger als ILogger. Maar misschien wil ik DatabaseLogger injecteren.
  3. Elke keer als ik wil veranderen welke objecten mijn AClass nodig heeft, moet ik nu op twee plaatsen kijken: de klasse zelf en het configuratiebestand. Hoe maakt dat het leven gemakkelijker?
  4. Als Property van AClass niet wordt geïnjecteerd, is het dan moeilijker om het uit te spotten?
  5. Terug naar de eerste vraag. Als het gebruik van nieuw object () slecht is, hoe komt het dan dat we de implementatie injecteren en niet de interface? Ik denk dat velen van jullie zeggen dat we in feite de interface injecteren, maar de configuratie laat je de implementatie van die interface specificeren ..niet tijdens runtime .. het is hard gecodeerd tijdens het compileren.

Dit is gebaseerd op het antwoord dat @Adam N heeft gepost.

Waarom hoeft PersonService zich geen zorgen meer te maken over GroupMembershipService? Je zei net dat GroupMembership meerdere dingen (objecten/eigenschappen) heeft waarvan het afhankelijk is. Als GMService vereist was in PService, zou je het als eigendom hebben. Je kunt dat uitlachen, ongeacht of je het hebt geïnjecteerd of niet. De enige keer dat ik wil dat het wordt geïnjecteerd, is als GMService meer specifieke kinderklassen heeft, die je pas tijdens runtime zou weten. Dan zou je de subklasse willen injecteren. Of als je dat als singleton of prototype wilt gebruiken. Om eerlijk te zijn, het configuratiebestand heeft alles hard gecodeerd tot welke subklasse voor een type (interface) het tijdens het compileren gaat injecteren.

BEWERKEN

Een leuke opmerking van Jose Maria Arranz op DI

DI verhoogt de cohesie door de noodzaak om de richting van afhankelijkheid te bepalen en het schrijven van lijmcode weg te nemen.

Niet waar. De richting van afhankelijkheden is in XML-vorm of als annotaties, uw afhankelijkheden worden geschreven als XML-code en annotaties. XML en annotaties ZIJN broncode.

DI vermindert de koppeling door al uw componenten modulair (d.w.z. vervangbaar) te maken en goed gedefinieerde interfaces met elkaar te hebben.

Niet waar. Je hebt geen DI-framework nodig om een ​​modulaire code te bouwen op basis van interfaces.

Over vervangbaar: met een heel eenvoudig .properties-archief en Class.forName kun je definiëren welke klassen kunnen veranderen. Als ELKE klasse van uw code kan worden gewijzigd, is Java niets voor u, gebruik een scripttaal. Overigens: annotaties kunnen niet worden gewijzigd zonder opnieuw te compileren.

Naar mijn mening is er maar één reden voor DI-frameworks: ketelplaatreductie. Met een goed uitgevoerd fabriekssysteem kunt u hetzelfde doen, meer gecontroleerd en voorspelbaarder als uw favoriete DI-framework, DI-frameworks beloven codereductie (XML en annotaties zijn ook broncode). Het probleem is dat deze boilerplate-reductie gewoon echt is in heel erg eenvoudige gevallen (één instantie per klasse en vergelijkbaar), soms is het in de echte wereld het kiezen van het juiste serviceobject niet zo eenvoudig als het toewijzen van een klasse aan een singleton-object.


Antwoord 21

De populaire antwoorden zijn nutteloos, omdat ze afhankelijkheidsinjectie definiëren op een manier die niet nuttig is. Laten we het erover eens zijn dat we met “afhankelijkheid” een reeds bestaand ander object bedoelen dat ons object X nodig heeft. Maar we zeggen niet dat we aan ‘afhankelijkheidsinjectie’ doen als we zeggen

$foo = Foo->new($bar);

We noemen dat het doorgeven van parameters aan de constructor. Dat doen we al regelmatig sinds de constructeurs zijn uitgevonden.

‘Injectie van afhankelijkheid’ wordt beschouwd als een soort ‘inversie van controle’, wat betekent dat er enige logica uit de beller wordt gehaald. Dat is niet het geval wanneer de beller parameters doorgeeft, dus als dat DI was, zou DI geen inversie van de controle impliceren.

DI betekent dat er een tussenniveau is tussen de aanroeper en de constructor die afhankelijkheden beheert. Een Makefile is een eenvoudig voorbeeld van afhankelijkheidsinjectie. De “beller” is de persoon die “make bar” typt op de opdrachtregel en de “constructor” is de compiler. De Makefile specificeert dat de balk afhankelijk is van foo, en het doet een

gcc -c foo.cpp; gcc -c bar.cpp

voordat je een

. doet

gcc foo.o bar.o -o bar

De persoon die ‘make bar’ typt, hoeft niet te weten dat die balk afhankelijk is van foo. De afhankelijkheid is geïnjecteerd tussen “make bar” en gcc.

Het belangrijkste doel van het tussenliggende niveau is niet alleen om de afhankelijkheden door te geven aan de constructor, maar om alle afhankelijkheden op slechts één plaats op te sommen en ze voor de codeur te verbergen (niet om laat de codeur ze verstrekken).

Meestal biedt het tussenliggende niveau fabrieken voor de geconstrueerde objecten, die een rol moeten bieden waaraan elk gevraagd objecttype moet voldoen. Dat komt omdat je door een gemiddeld niveau te hebben dat de details van de constructie verbergt, je al de abstractieboete hebt opgelegd die door fabrieken is opgelegd, dus je kunt net zo goed fabrieken gebruiken.


Antwoord 22

Dependency Injection betekent een manier (eigenlijk elke manier) voor een deel van de code (bijv. een klasse) om op een modulaire manier toegang te hebben tot afhankelijkheden (andere delen van code, bijv. andere klassen, waarvan het afhankelijk is) zonder dat ze hardgecodeerd zijn (zodat ze vrij kunnen worden gewijzigd of overschreven, of zelfs op een ander moment kunnen worden geladen, indien nodig)

(en ps, ja het is een overdreven gehypte naam van 25$ geworden voor een vrij eenvoudig concept), mijn .25 cent


Antwoord 23

Injectie met afhankelijkheid is een mogelijke oplossing voor wat in het algemeen de eis van “afhankelijkheidsverduistering” zou kunnen worden genoemd. Afhankelijkheidsverduistering is een methode om de ‘voor de hand liggende’ aard uit het proces van het verschaffen van een afhankelijkheid aan een klasse die dit vereist te verwijderen en daardoor op de een of andere manier de voorziening van die afhankelijkheid aan die klasse te verdoezelen. Dit is niet noodzakelijk een slechte zaak. Door de manier waarop een afhankelijkheid aan een klasse wordt geleverd te verdoezelen, is iets buiten de klasse verantwoordelijk voor het creëren van de afhankelijkheid, wat betekent dat in verschillende scenario’s een andere implementatie van de afhankelijkheid aan de klasse kan worden geleverd zonder wijzigingen aan te brengen naar de klas. Dit is geweldig om te schakelen tussen productie- en testmodi (bijv. door een ‘schijnservice’-afhankelijkheid te gebruiken).

Helaas is het slechte deel dat sommige mensen hebben aangenomen dat je een gespecialiseerd raamwerk nodig hebt om afhankelijkheidsverduistering uit te voeren en dat je op de een of andere manier een ‘mindere’ programmeur bent als je ervoor kiest om een ​​bepaald raamwerk niet te gebruiken om het te doen. Een andere, uiterst verontrustende mythe, die door velen wordt geloofd, is dat injectie met afhankelijkheid de enige manier is om afhankelijkheid te verdoezelen. Dit is aantoonbaar en historisch en duidelijk 100% verkeerd, maar u zult moeite hebben om sommige mensen ervan te overtuigen dat er alternatieven zijn voor afhankelijkheidsinjectie voor uw vereisten voor het verduisteren van afhankelijkheid.

Programmeurs begrijpen al jaren dat afhankelijkheidsverduistering vereist is en er zijn veel alternatieve oplossingen ontwikkeld, zowel voor als nadat afhankelijkheidsinjectie werd bedacht. Er zijn fabriekspatronen, maar er zijn ook veel opties voor het gebruik van ThreadLocal waarbij geen injectie in een bepaalde instantie nodig is – de afhankelijkheid wordt effectief in de thread geïnjecteerd, wat het voordeel heeft dat het object beschikbaar wordt gemaakt (via handige statische gettermethoden) voor elke klasse die het vereist zonder annotaties toe te voegen aan de klassen die het nodig hebben en ingewikkelde XML ‘lijm’ in te stellen om het mogelijk te maken. Wanneer uw afhankelijkheden vereist zijn voor persistentie (JPA/JDO of wat dan ook), kunt u ‘transparante persistentie’ veel gemakkelijker bereiken en met domeinmodel- en bedrijfsmodelklassen die puur uit POJO’s bestaan ​​(dwz geen framework-specifiek/opgesloten in annotaties).


Antwoord 24

Uit het boek, ‘Goed gegronde Java-ontwikkelaar: essentiële technieken van Java 7 en polyglot programmeren

DI is een bepaalde vorm van IoC, waarbij het proces van het vinden van je afhankelijkheden is
buiten de directe controle van uw momenteel uitgevoerde code.


Antwoord 25

Injectie met afhankelijkheid voor 5-jarigen.

Als je zelf dingen uit de koelkast gaat halen, kun je problemen veroorzaken. Misschien laat je de deur openstaan, misschien krijg je iets wat mama of papa niet wil. Misschien ben je zelfs op zoek naar iets dat we niet eens hebben of dat is verlopen.

Wat je zou moeten doen, is een behoefte aangeven: “Ik heb iets te drinken nodig bij de lunch”, en dan zorgen we ervoor dat je iets hebt als je gaat zitten om te eten.


Antwoord 26

uit Boek Apress.Spring.Persistence.with.Hibernate.Oct.2010

Het doel van afhankelijkheidsinjectie is om het werk van
het oplossen van externe softwarecomponenten van uw applicatiebedrijf
logica. Zonder afhankelijkheidsinjectie, de details van hoe een component
toegang heeft tot de vereiste services kan in de war raken met de componenten
code. Dit vergroot niet alleen de kans op fouten, het voegt code toe
bloat, en vergroot onderhoudscomplexiteit; het koppelt componenten
nauwer samen, waardoor het moeilijk is om afhankelijkheden te wijzigen wanneer:
refactoring of testen.


Antwoord 27

Dependency Injection (DI) maakt deel uit van de Dependency Inversion Principle (DIP)-praktijk, ook wel Inversion of Control (IoC) genoemd. In principe moet je DIP doen omdat je je code meer modulair en unit-testable wilt maken, in plaats van slechts één monolithisch systeem. Dus je begint met het identificeren van delen van de code die kunnen worden gescheiden van de klas en worden weggeabstraheerd. Nu moet de implementatie van de abstractie van buiten de klas worden geïnjecteerd. Normaal gesproken kan dit worden gedaan via constructor. Dus je maakt een constructor die de abstractie als parameter accepteert, en dit wordt afhankelijkheidsinjectie genoemd (via constructor). Voor meer uitleg over DIP-, DI- en IoC-containers kun je Hier


Antwoord 28

Dependency Injection (DI) is er een van Design Patterns, die het basiskenmerk van OOP gebruikt: de relatie in het ene object met een ander object. Terwijl overerving het ene object erft om een ​​ander object complexer en specifieker te maken, creëert een relatie of associatie eenvoudigweg een verwijzing naar een ander object van het ene object met behulp van attribuut. De kracht van DI is in combinatie met andere functies van OOP, net als interfaces en het verbergen van code.
Stel, we hebben een klant (abonnee) in de bibliotheek, die voor de eenvoud maar één boek kan lenen.

Interface van boek:

package com.deepam.hidden;
public interface BookInterface {
public BookInterface setHeight(int height);
public BookInterface setPages(int pages);   
public int getHeight();
public int getPages();  
public String toString();
}

Vervolgens kunnen we veel soorten boeken hebben; een van de typen is fictie:

package com.deepam.hidden;
public class FictionBook implements BookInterface {
int height = 0; // height in cm
int pages = 0; // number of pages
/** constructor */
public FictionBook() {
    // TODO Auto-generated constructor stub
}
@Override
public FictionBook setHeight(int height) {
  this.height = height;
  return this;
}
@Override
public FictionBook setPages(int pages) {
  this.pages = pages;
  return this;      
}
@Override
public int getHeight() {
    // TODO Auto-generated method stub
    return height;
}
@Override
public int getPages() {
    // TODO Auto-generated method stub
    return pages;
}
@Override
public String toString(){
    return ("height: " + height + ", " + "pages: " + pages);
}
}

Abonnee kan nu een koppeling met het boek hebben:

package com.deepam.hidden;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Subscriber {
BookInterface book;
/** constructor*/
public Subscriber() {
    // TODO Auto-generated constructor stub
}
// injection I
public void setBook(BookInterface book) {
    this.book = book;
}
// injection II
public BookInterface setBook(String bookName) {
    try {
        Class<?> cl = Class.forName(bookName);
        Constructor<?> constructor = cl.getConstructor(); // use it for parameters in constructor
        BookInterface book = (BookInterface) constructor.newInstance();
        //book = (BookInterface) Class.forName(bookName).newInstance();
    } catch (InstantiationException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (SecurityException e) {
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    }
    return book;
}
public BookInterface getBook() {
  return book;
}
public static void main(String[] args) {
}
}

Alle drie de klassen kunnen worden verborgen voor hun eigen implementatie. Nu kunnen we deze code gebruiken voor DI:

package com.deepam.implement;
import com.deepam.hidden.Subscriber;
import com.deepam.hidden.FictionBook;
public class CallHiddenImplBook {
public CallHiddenImplBook() {
    // TODO Auto-generated constructor stub
}
public void doIt() {
    Subscriber ab = new Subscriber();
    // injection I
    FictionBook bookI = new FictionBook();
    bookI.setHeight(30); // cm
    bookI.setPages(250);
    ab.setBook(bookI); // inject
    System.out.println("injection I " + ab.getBook().toString());
    // injection II
    FictionBook bookII = ((FictionBook) ab.setBook("com.deepam.hidden.FictionBook")).setHeight(5).setPages(108); // inject and set
    System.out.println("injection II " + ab.getBook().toString());      
}
public static void main(String[] args) {
    CallHiddenImplBook kh = new CallHiddenImplBook();
    kh.doIt();
}
}

Er zijn veel verschillende manieren om afhankelijkheidsinjectie te gebruiken. Het is mogelijk om het te combineren met Singleton, enz., maar nog steeds in de basis wordt het alleen gerealiseerd door een attribuut van een objecttype in een ander object te creëren.
Het nut is alleen en alleen in functie, die code, die we steeds opnieuw moeten schrijven, wordt altijd voor ons voorbereid en gedaan. Dit is de reden waarom DI zo nauw verbonden is met Inversion of Control (IoC), wat betekent dat ons programma de controle doorgeeft aan een andere actieve module, die bonen in onze code injecteert. (Elk object, dat kan worden geïnjecteerd, kan worden ondertekend of als een Bean worden beschouwd.) In de lente wordt dit bijvoorbeeld gedaan door een ApplicationContext-container te maken en te initialiseren, die dit werk voor ons doet. We creëren eenvoudig in onze code de context en roepen initialisatie van de bonen op. Op dat moment is de injectie automatisch gedaan.


Antwoord 29

Uit Christoffer Noring, Pablo Deeleman’s boek Learning Angular – Second Edition :

“Naarmate onze applicaties groeien en evolueren, heeft elk van onze code-entiteiten intern instances van andere objecten nodig, die beter bekend zijn als afhankelijkheden in de wereld van software-engineering. De actie van het doorgeven van dergelijke afhankelijkheden aan de afhankelijke klant staat bekend als injectie, en het houdt ook de deelname in van een andere code-entiteit, genaamd de injector. De injector zal de verantwoordelijkheid nemen voor instantieren en bootstrapping de vereiste afhankelijkheden zodat ze klaar zijn voor gebruik vanaf het moment dat ze met succes in de klant zijn geïnjecteerd. Dit is erg belangrijk omdat de klant niets weet over hoe hij instantiëring moet maken zijn eigen afhankelijkheden en is zich alleen bewust van de interface die ze implementeren om ze te gebruiken.”

Van: Anton Moiseev. boek Angular Development with Typescript, Second Edition. :

Kortom, DI helpt u bij het schrijven van code op een losjes gekoppelde manier en maakt uw code meer testbaar en herbruikbaar.


Antwoord 30

In eenvoudige bewoordingen is afhankelijkheidsinjectie (DI) de manier om afhankelijkheden of nauwe koppeling tussen verschillende objecten te verwijderen. Dependency Injection geeft een samenhangend gedrag aan elk object.

DI is de implementatie van het IOC-principe van Spring dat zegt: “Bel ons niet, we zullen u bellen”. Het gebruik van afhankelijkheidsinjectie-programmeur hoeft geen object te maken met het nieuwe sleutelwoord.

Objecten worden eenmaal in de Spring-container geladen en vervolgens hergebruiken we ze wanneer we ze nodig hebben door die objecten op te halen uit de Spring-container met de methode getBean(String beanName).

LEAVE A REPLY

Please enter your comment!
Please enter your name here

eighteen + thirteen =

Other episodes