Au fost mai multe întrebări postat deja cu întrebări specifice despre de injectare dependență, cum ar fi atunci când să-l folosească și ce cadrele sunt acolo pentru ea. Cu toate acestea,
Ce este dependența de injecție și când/de ce ar trebui sau n-ar trebui't fi utilizate?
Cea mai bună definiție I'am găsit până acum este unul de James Mal:
"de Injectare Dependență" este de 25 de dolari termen de 5 procente concept. [...] de injectare Dependență înseamnă a da o obiect sale variabile de instanta. [...].
Acolo este un articol de Martin Fowler, care se pot dovedi utile, de asemenea.
Injectare dependență este practic, oferind obiecte care un obiect are nevoie (dependențele sale), în loc de a avea de a construi-le în sine. L's o tehnică utilă pentru testare, deoarece permite dependențe de a fi batjocorit sau stinsă.
Dependențele pot fi injectate în obiecte prin mai multe mijloace (cum ar fi constructor de injecție sau setter injecție). Se poate folosi chiar și de specialitate de injectare dependență cadre (de exemplu, Primavara), pentru a face asta, dar cu siguranță ei nu sunt't este necesar. Nu't nevoie de aceste cadre pentru a avea de injectare dependență. Instantierea și trece obiecte (dependențe) în mod explicit este ca o injecție injecție de cadru.
Injectare dependență trece dependența la alte obiecte sau cadru( dependența de injector).
Dependența de injecție face mai ușor de testare. Injectarea se poate face prin constructor.
SomeClass()
are constructor, după cum urmează:
public SomeClass() {
myObject = Factory.getObject();
}
Problema**:
Dacă myObject implică sarcini complexe, cum ar fi accesul la disc sau de acces la rețea, este tare pentru a face unitatea de încercare pe SomeClass(). Programatorii trebuie să batjocorească
myObject` și s-ar putea intercepta** fabrica de apel.
Soluție alternativă:
myObject
în ca argument pentru constructorpublic SomeClass (MyClass myObject) {
this.myObject = myObject;
}
myObject
pot fi transmise direct, ceea ce face mai ușor de testare.
Este mai greu pentru a izola componentele în unitate de testare fără injectare dependență.
În 2013, când am scris acest răspuns, aceasta a fost o temă majoră pe Google de Testare Blog. Acesta rămâne cel mai mare avantaj pentru mine, ca programatori nu întotdeauna nevoie de un plus de flexibilitate în run-time de proiectare (de exemplu, pentru servicii de localizare sau modele similare). Programatorii de multe ori nevoie pentru a izola clase în timpul testării.
Am găsit acest amuzant exemplu în ceea ce privește cuplarea:
Orice cerere este compus din mai multe obiecte care colaborează cu alte fiecare pentru a efectua unele lucruri utile. În mod tradițional, fiecare obiect este responsabil pentru obținerea propriile referințe la obiecte dependente (dependențe) a colabora cu. Acest lucru duce la extrem cuplat clase și greu să-cod de încercare.
De exemplu, ia în considerare un " Car " de obiect.
Un " Car " depinde de jante, motor, combustibil, baterie, etc. pentru a rula. În mod tradițional, vom defini brandul de astfel de dependente de obiecte, împreună cu definiția de "Auto" obiect.
Fără Injectare Dependență (DI):
class Car{
private Wheel wh = new NepaliRubberWheel();
private Battery bt = new ExcideBattery();
//The rest
}
Aici, "Auto" obiect este responsabil pentru crearea obiectelor dependente.
Dacă vrem să schimbăm tipul de dependente de obiect - spune "Roată" - după ce inițial NepaliRubberWheel()
înțepături?
Avem nevoie pentru a recrea Masina obiect cu noul său dependență spune ChineseRubberWheel()
, dar numai "Auto" producătorul poate face asta.
Atunci de ce nu `Injectare Dependență faci pentru noi...?
Atunci când se utilizează dependența de injecție, obiectele sunt prezentate dependențele în timpul rulării, mai degrabă decât de compilare (masina de fabricație timp). Așa că acum putem schimba "Roată" ori de câte ori vrem. Aici, "dependență" ("roata") pot fi injectate în "Auto" în timpul rulării.
După utilizarea de injectare dependență:
Aici, suntem injectarea la dependențe (Roată și Baterie) la runtime. Prin urmare, termenul de : Injectare Dependență.
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;
}
}
Dependența de Injecție este o practică în cazul în care obiectele sunt proiectate într-o manieră în care acestea primesc instanțe de obiecte din alte bucati de cod, în loc de a construi-le pe plan intern. Acest lucru înseamnă că orice obiect de punere în aplicare a interfeței, care este impus de obiect poate fi înlocuit fără a schimba codul, care simplifică testarea, și îmbunătățește decuplare.
De exemplu, ia în considerare aceste clase:
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 ) { ... }
}
În acest exemplu, punerea în aplicare a PersonService::addManager " și " PersonService::removeManager
ar avea nevoie de un exemplu de GroupMembershipService în scopul de a face activitatea sa. Fără Injectare Dependență, modul tradițional de a face acest lucru ar fi pentru a instantia un nou GroupMembershipService
în constructorul de PersonServiceși de a folosi ca exemplu atribut în ambele funcții. Cu toate acestea, dacă constructorul a
GroupMembershipServiceare mai multe lucruri de care are nevoie, sau, mai rău încă, există unele inițializare "setteri" care trebuie să fie numit pe
GroupMembershipService, codul creste destul de repede, iar
PersonService acum nu depinde numai de GroupMembershipService dar, de asemenea, orice altceva care
GroupMembershipServicedepinde. În plus, legătura cu
GroupMembershipServiceeste hardcoded în
PersonServiceceea ce înseamnă că puteți't "inactiv" o
GroupMembershipService pentru scopuri de testare, sau de a folosi un model de strategie în diferite părți ale aplicației.
Cu Injecție de Dependență, în loc de instantierea GroupMembershipService
în PersonService
, ai'd fie trece-l la PersonService
constructor, sau a adăuga o Proprietate (getter și setter) pentru a seta o instanță locală de ea. Acest lucru înseamnă că PersonService
nu mai trebuie să vă faceți griji despre cum să creați o GroupMembershipService
, se acceptă doar cei care-l's-a dat, și lucrează cu ei. Acest lucru înseamnă, de asemenea, că ceva care este o subclasă a GroupMembershipService
, sau pune în aplicare GroupMembershipService
de interfață poate fi "injectat" în PersonService", iar " PersonService
nu't nevoie să știți despre schimbare.
Acceptat răspunsul este unul bun - dar aș dori să adăugați la aceasta faptul că DI este foarte mult, cum ar fi clasic evitarea predeterminate constante în cod.
Când utilizați unele constant ca un nume de bază de date te'd repede se muta din interior de codul de la un fișier de configurare și să treacă o variabilă care conține acea valoare la locul unde este nevoie. Motivul pentru care fac asta este că aceste constante schimba, de obicei, mult mai frecvent decât restul codului. De exemplu, dacă'd place să testați codul într-o bază de date de test.
DI este analog cu aceasta în lumea de programare Orientate Obiect. Valorile acolo în loc de constante literale sunt obiecte întregi - dar motivul pentru a muta codul crearea i-a scos din clasa cod este similar - obiecte schimba mai frecvent atunci codul care le folosește. Un caz important în cazul în care o astfel de schimbare este nevoie de teste.
Las's încercați exemplu simplu cu Masina și Motor clase, orice mașină are nevoie de un motor pentru a merge oriunde, cel puțin pentru moment. Deci, codul de mai jos cum va arata fara injectare dependență.
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!");
}
}
Și pentru a instantia clasa de Masina vom folosi următorul cod:
Car car = new Car();
Problema cu acest cod pe care ne-am strâns cuplat la GasEngine și dacă vom decide să-l schimbe la ElectricityEngine atunci va trebui să rescrie clasa Auto. Și mai mare a cererii mai multe probleme si dureri de cap, vom avea pentru a adăuga și de a folosi noul tip de motor.
Cu alte cuvinte, cu această abordare este că nivelul nostru ridicat Masina de clasa este dependentă de nivelul inferior GasEngine clasa care încalcă Dependență Inversiune Principiu(DIP) din SOLID. DIP sugerează că ar trebui să depindă de abstracții, nu clasele de beton. Deci, pentru a satisface aceasta vom introduce IEngine interfață și rescrie codul de mai jos:
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();
}
}
Acum Mașina noastră de clasă este dependentă numai IEngine interfață, nu o aplicare specifică a motorului. Acum, singurul truc este cum putem crea o instanță din Mașină și să-l un real Motor de beton clasa ca GasEngine sau ElectricityEngine. Ca's unde Injectare Dependență vine în.
Car gasCar = new Car(new GasEngine());
gasCar.Run();
Car electroCar = new Car(new ElectricityEngine());
electroCar.Run();
Aici vom injecta practic(de trecere) dependența noastră(Motor de exemplu) pentru Masina de constructor. Deci, acum, cursurile noastre au cuplaj slab între obiecte și dependințele lor, și putem adăuga cu ușurință noi tipuri de motoare, fără a schimba clasa de Masina.
Principalul beneficiu al Injectare Dependență care clasele sunt mai slab cuplate, pentru că ei nu au hard-coded dependențe. Acest lucru rezultă Dependența Inversiune Principiu, care a fost menționat mai sus. În loc de referențiere implementări specifice, cursuri de cerere de abstractizare (de obicei interfețe) care le sunt furnizate, atunci când clasa este construit.
Deci, în final injectare Dependență este doar o tehnică pentru realizarea cuplaj slab între obiecte și dependințele lor. mai Degrabă decât direct instantierea dependențe ce clasa are nevoie în pentru a efectua acțiunile sale, dependențele sunt prevăzute la clasa (cel mai adesea) prin injectare constructor.
De asemenea, atunci când avem multe dependențe este foarte bună practică de a utiliza Inversiune de Control(IoC) recipiente care ne poate spune care interfețele trebuie să fie mapate la care implementari concrete pentru toate dependențele și putem rezolva aceste dependențe pentru noi, atunci când se construiește obiectul nostru. De exemplu, am putea specifica în cartografiere pentru Cio recipient că IEngine dependență ar trebui să fie mapate la GasEngine clasa și atunci când ne întrebăm Cio container pentru o instanță a noastră Masina clasa, automat va construi Masina clasa a cu o GasEngine dependența de trecut în.
ACTUALIZARE: Privit curs despre EF Bază de Julie Lerman recent și plăcut, de asemenea, o scurtă definiție despre DI.
de injectare Dependență este un model pentru a permite cererea dumneavoastră pentru a injecta obiecte pe zbura pentru clasele care au nevoie de ele, fără a forța pe cei clase să fie responsabil pentru aceste obiecte. Acesta vă permite codul tău de a fi mai slab cuplate, și Entity Framework dopuri în acest fel sistemul de servicii.
Las's imaginați-vă că doriți să du-te de pescuit:
Fără injecție de dependență, aveți nevoie pentru a avea grijă de totul singur. Aveți nevoie pentru a găsi o barcă, pentru a cumpăra o tijă de pescuit, să se uite pentru momeala, etc. L's posibil, desigur, dar pune o mulțime de responsabilitate. În software-ul de termeni, aceasta înseamnă că trebuie să efectuați o căutare pentru toate aceste lucruri.
Cu injectare dependență, altcineva se ocupă de pregătirea și de a face cele necesare echipamente disponibile pentru tine. Veți primi ("fi injectat") barca, tijă de pescuit și momeală - toate gata de utilizare.
Acest este cea mai simplă explicație despre Injectare Dependență și Injectare Dependență Container am văzut-o vreodată:
Injectare dependență și Injectare dependență Containere sunt lucruri diferite:
Nu't nevoie de un recipient pentru a face injectare dependență. Cu toate acestea, un container poate ajuta.
Nu't "de injectare dependență" înseamnă doar folosind parametrizate constructori și organisme publice?
James Mal's articolul prezintă următoarele exemple pentru comparație.
Constructorul fără injectare dependență:
public class Example {
private DatabaseThingie myDatabase;
public Example() {
myDatabase = new DatabaseThingie();
}
public void doStuff() {
...
myDatabase.getData();
...
}
}
Constructor cu injecție de dependență:
public class Example {
private DatabaseThingie myDatabase;
public Example(DatabaseThingie useThisDatabaseInstead) {
myDatabase = useThisDatabaseInstead;
}
public void doStuff() {
...
myDatabase.getData();
...
}
}
Pentru a face Injectare Dependență concept simplu de înțeles. Las's ia un exemplu de butonul de comutare pentru a comuta(on/off) - un bec.
Comutatorul trebuie să știe dinainte ce bec am conectat la (hard-coded dependență). Deci,
Switch -> PermanentBulb //comutator este conectat direct la o permanentă bec, testarea nu este posibilă cu ușurință
Switch(){
PermanentBulb = new Bulb();
PermanentBulb.Toggle();
}
Comutator știe doar am nevoie pentru a activa/dezactiva oricare Bec este trecut la mine. Deci,
Switch -> Bulb1 SAU Bulb2 SAU NightBulb (injectat dependență)
Switch(AnyBulb){ //pass it whichever bulb you like
AnyBulb.Toggle();
}
Modificarea James De Exemplu pentru Întrerupător și Bec:
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();
...
}
}`
Ce este Dependența de Injecție (DI)?
Cum au spus și alții, Dependența de Injecție(DI) elimină responsabilitatea directă crearea și gestionarea de viață, de alte cazuri de obiecte pe care clasa noastră de interes (clasa de consum) este dependent (în UML sens). Aceste cazuri sunt, în schimb, a trecut la nostru clasa de consum, de obicei, ca parametri constructor sau prin intermediul proprietate setteri (gestionarea dependenței obiect instanțiere și de a trece la clasa de consum este de obicei efectuată de către un Inversiune de Control (IoC) container, dar care'e un alt subiect).
DI, BAIE și SOLIDE
În mod special, în paradigma de Robert c. Martin's principii SOLIDE de Proiectare Orientată Obiect, DI
este una dintre cele posibile implementări ale Dependență Inversiune Principiu (DIP). De DIP este " D " de "SOLID" mantra - alte DIP implementări include Servicii de Localizare, și Plugin modele.
Obiectivul de IMERSIE este de a decupla strans, beton dependențe între clase, și în loc, să slăbiți cuplare printr-o abstracție, care poate fi realizat printr-o "interfață", `clasă abstractă " sau " virtuale pure de clasă, în funcție de limbă și de abordarea utilizată.
Fără BAIE, codul nostru (I'am numit acest 'consumatoare de clasa') este cuplată direct la un beton de dependență și este, de asemenea, de multe ori împovărate cu responsabilitatea de a ști cum să obțină, și de a gestiona, un exemplu de această dependență, adică din punct de vedere conceptual:
"I need to create/use a Foo and invoke method `GetBar()`"
Întrucât după aplicarea BAIE, cerința este slăbit, și preocuparea de a obține și de gestionarea durata de viata de " Foo " dependență a fost eliminat:
"I need to invoke something which offers `GetBar()`"
De ce să folosiți DIP (și DI)? Decuplarea dependențe între clase în acest mod vă permite pentru ușor de substituție de aceste dependență clase cu alte implementari care, de asemenea, să îndeplinească condițiile de abstractizare (de exemplu, dependența poate fi schimbat cu un alt punerea în aplicare de aceeași interfață). Mai mult decât atât, după cum s-a menționat, eventual ** de cel mai bun motiv de a decupla clase prin intermediul DIP este de a permite o consumatoare de clasă să fie testate în mod izolat, deoarece aceleași dependențe pot fi acum stinse și/sau batjocorit. O consecință a DI este că durata de viata de management de dependență obiect cazuri nu mai este controlată de o consumatoare de clasa, ca obiect dependența este acum trecut în consumatoare de clasă (prin constructor sau setter injecție). Acest lucru poate fi vizualizat în diferite moduri:
MyDepClass
este thread-safe - ce dacă am face un singleton și se injectează aceeași instanță în toți consumatorii?)
Exemplu
Aici's un simplu C# punere în aplicare. Dat fiind cele de mai jos Consumatoare de clasa: public class MyLogger
{
public void LogRecord(string somethingToLog)
{
Console.WriteLine("{0:HH:mm:ss} - {1}", DateTime.Now, somethingToLog);
}
}
Deși aparent inofensive, acesta are două "statice" dependențe pe alte două clase, Sistemul.DateTime " și " Sistemul.Consola
, care nu numai că limitează exploatarea opțiuni de ieșire (de logare pentru consola va fi lipsit de valoare dacă nu te vede nimeni), dar mai rău, este dificil de a testa în mod automat dat dependența de un non-deterministe ceasul de sistem.
Putem totuși să se aplice "BAIE" la această clasă, prin abstractizare din preocuparea de marcare temporala ca o dependență, și de cuplare MyLogger
doar la o simplă interfață:
public interface IClock
{
DateTime Now { get; }
}
Putem, de asemenea, slăbi dependența Consola
la o abstractizare, cum ar fi un Textier. Dependența de Injecție este de obicei implementat ca fie
constructorulinjecție (trece o abstracție pentru a o dependență ca un parametru pentru constructorul de o consumatoare de clasa) sau
Setter de Injectare (trecerea de la dependență prin o setXyz()
setter sau .Net Proprietate cu {set;}
definit). Constructor de Injecție este de preferat, deoarece acest lucru garantează clasa va fi într-o corectă stat după construcție, și vă permite interne dependență câmpuri să fie marcate ca readonly
(C#) sau "finală" (Java). Deci, folosind constructorul de injecție pe exemplul de mai sus, acest lucru ne lasă cu:
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);
}
}
(Un beton "Ceas" trebuie să fie furnizate, care, desigur, ar putea reveni la `DateTime.Acum, și cele două dependențe trebuie să fie furnizate de către un IoC recipient prin injectare constructor) Un sistem automat de Unitate de Testare poate fi construit, care să dovedească definitiv nostru logger este de lucru corect, ca acum avem control asupra dependențele - în timp, și ne poate spiona pe cele scrise de ieșire:
[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());
}
*Pașii Următori Dependența de injecție este invariabil asociat cu o Inversiune de Control container(Cio)](http://martinfowler.com/articles/injection.html), pentru a injecta (oferi) beton dependență de situații și de a gestiona durata de viata de cazuri. Timpul de configurare / punere proces, " IoC " containere permite ca următoarele să fie definite:
IBar
, a reveni o ConcreteBar
de exemplu") IDisposable
și va lua cu privire la responsabilitatea de Eliminare
dependențe în conformitate cu configurat durata de viata de management.
De obicei, o dată IoC containere au fost configurate / procesul de bootstrap, ele funcționează perfect în fundal, permițându-coder să se concentreze pe codul de la mână, mai degrabă decât îngrijorătoare despre dependențe. cheia De la DI-friendly cod este de a evita static de cuplare de clase, și nu de a utiliza noi() pentru crearea de Dependențe Ca pe exemplul de mai sus, decuplarea de dependențe are nevoie de un efort de proiectare și pentru producător, există o schimbare de paradigmă nevoie de a sparge obiceiul de
nou ' ing dependențe direct, și în loc să avem încredere în container pentru a gestiona dependențe. Dar beneficiile sunt multe, mai ales în capacitatea de a testa temeinic clasa ta de interes. **Notă** : crearea / cartografiere / proiecție (prin
noi ..()`) a POCO / POJO / Serialization DTOs / Entități Grafice / Anonim JSON proiecții et al - de exemplu, "de Date numai" clase sau înregistrează - a folosit sau a revenit la metode sunt nu considerată ca Dependențe (în UML sens) și nu pot să DI. Folosind " noi " pentru proiect acestea este bine.
Ideea de Injectare Dependență (DI) este de a menține codul sursa al aplicatiei curat și stabil:
Practic, fiecare model de design separă preocupări pentru a face schimbări viitoare afecta minim fișiere.
Specifice domeniului de DI este delegarea de dependență de configurația și de inițializare.
Dacă lucrezi ocazional in afara de Java, amintesc cât de "sursa" este adesea folosit în multe limbaje de scripting (Shell, Tcl, etc., sau chiar de "import" în Python utilizate în mod abuziv pentru acest scop).
Considerăm simple dependent.sh` script:
#!/bin/sh
# Dependent
touch "one.txt" "two.txt"
archive_files "one.txt" "two.txt"
Script-ul este dependent: nu se va't executa cu succes pe cont propriu (archive_files
nu este definit).
Ai defini `archive_files " în "archive_files_zip.sh implementarea script (folosind" zip " în acest caz):
#!/bin/sh
# Dependency
function archive_files {
zip files.zip "$@"
}
În loc de "sursă" -ing implementarea script direct în dependente unul, utilizați o injector.sh
"recipient" care înfășoară ambele "componente":
#!/bin/sh
# Injector
source ./archive_files_zip.sh
source ./dependent.sh
Anii archive_files
dependență a fost injectat în dependent script-ul.
Ai putea fi injectat dependență care implementează archive_files "folosind" tar " sau " xz
.
Dacă dependent.sh` script-ul folosit dependențe direct, abordarea va fi numit dependența de căutare (care este opusul a injectare dependență):
#!/bin/sh
# Dependent
# dependency look-up
source ./archive_files_zip.sh
touch "one.txt" "two.txt"
archive_files "one.txt" "two.txt"
Acum problema este ca dependent "component" a de a efectua inițializarea sine.
"component"'s codul sursă nu este nici curat nici stabil pentru orice modificări în inițializare de dependențe necesită o nouă versiune pentru "componente"'s fișier de cod sursă la fel de bine.
DI nu este la fel de mare măsură subliniat și popularizat ca și în Java cadre.
Dar's o abordare generică a împărți preocupările de:
aplicarea de dezvoltare (single codul sursă de lansare a ciclului de viață) aplicarea implementare (mai multe țintă medii cu ciclurile de viață independentă)
Folosind configurația numai cu dependency lookup nu ajuta ca număr de parametri de configurare se poate schimba pe dependență (de exemplu, noul tip de autentificare) precum și numărul de tipuri acceptate de dependențe (de exemplu, nou tip de bază de date).
Toate raspunsurile de mai sus sunt bune, scopul meu este de a explica conceptul într-un mod simplu, astfel încât oricine, fără cunoștințe de programare poate înțelege, de asemenea, conceptul
Dependența de injecție este una dintre model de design care ne ajuta pentru a crea sisteme complexe într-o mai simplu mod.
Putem vedea o mare varietate de aplicarea acestui model în viața noastră de zi. Unele dintre exemple sunt casetofon, VCD, CD, etc.
Imaginea de mai sus este o imagine de magnetofon portabil casetofon, mijlocul secolului 20. Sursa.
Principala intenție de un casetofon de masina este de la înregistrare sau de redare a sunetului.
În timp ce proiectarea unui sistem este nevoie de o rolă de înregistrare sau de redare a sunetului sau muzica. Există două posibilități pentru proiectarea acestui sistem
Dacă vom folosi în primul rând avem nevoie pentru a deschide aparatul pentru a schimba bobina. dacă vom opta pentru cea de-a doua, care este plasarea unui carlig pentru role, primim un beneficiu suplimentar de a juca orice muzică prin schimbarea bobinei. și, de asemenea, funcția de reducere doar pentru a juca în tambur.
Ca înțelept injectare dependență este procesul de externalizare dependențele să se concentreze doar pe funcționalitatea specifică a componentelor, astfel încât componente independente pot fi cuplate împreună pentru a forma un sistem complex.
Principalele beneficii am realizat prin utilizarea de injectare dependență.
Acum o zi aceste concept constituie baza de bine-cunoscute de cadrele de programare lume. Primăvara Unghiulare, etc sunt bine-cunoscut software cadre construit pe partea de sus a acestui concept
Dependența de injecție este un model folosit pentru a crea instanțe de obiecte care alte obiecte se bazează pe fără să știe la compilare care clasa va fi folosit pentru a oferi această funcționalitate sau pur și simplu modul de injectare proprietăți de la un obiect se numește dependența de injecție.
Exemplu pentru Dependența de injecție
Anterior am scris ca acest cod
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();
}
Cu injecție de Dependență, dependența injector va scoate la instanțierea pentru noi
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();
}
Puteți citi, de asemenea,
Dependența de Injecție(DI) înseamnă a separa obiectele care sunt dependente una de alta. Spune că Un obiect este dependentă de Obiectul B deci, ideea este de a decupla aceste obiecte de la fiecare alte. Nu avem nevoie de codul de greu obiect folosind cuvinte cheie noi, mai degrabă schimbul de dependențe pentru obiecte în timpul rulării în ciuda de a compila timp. Dacă vorbim despre
Nu avem nevoie de codul de greu obiect folosind cuvinte cheie noi, mai degrabă defini bean dependență în fișierul de configurare. Containerul de primăvară va fi responsabil pentru prindere toate.
CIO este un concept general și poate fi exprimată în mai multe moduri diferite și Dependență de Injectare este un exemplu concret al CIO.
Constructor pe baza DI este realizat atunci când recipientul invocă un constructor de clasă cu un număr de argumente, fiecare reprezentând o dependența de o altă clasă.
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>
Setter-a bazat DI este realizat cu containerul de asteptare metode setter pe fasole după invocând un argument constructor sau nu-argument metodă statică fabrică pentru a instantia de fasole.
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"/>
NOTĂ: Este o regulă bună de degetul mare pentru a utiliza constructor argumente obligatorii dependențe și setteri pentru opțional dependențe. Rețineți că, dacă vom folosi adnotarea bazează mult de @este Necesar adnotare pe un setter poate fi folosit pentru a face setteri ca un dependențele necesare.
De exemplu, avem 2 clasa "Client" și "Serviciu". "Client" va folosi "Serviciu"
public class Service {
public void doSomeThingInService() {
// ...
}
}
Mod 1)
public class Client {
public void doSomeThingInClient() {
Service service = new Service();
service.doSomeThingInService();
}
}
Mod 2)
public class Client {
Service service = new Service();
public void doSomeThingInClient() {
service.doSomeThingInService();
}
}
Mod 3)
public class Client {
Service service;
public Client() {
service = new Service();
}
public void doSomeThingInClient() {
service.doSomeThingInService();
}
}
1) 2) 3) Folosind
Client client = new Client();
client.doSomeThingInService();
****Avantaje
Dezavantaje
Mod 1) Constructor de injecție
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();
}
}
Folosind
Client client = new Client(new Service());
// Client client = new Client(new Service(), new SqliteDatabase());
client.doSomeThingInClient();
Mod 2) Setter injecție
public class Client {
Service service;
public void setService(Service service) {
this.service = service;
}
public void doSomeThingInClient() {
service.doSomeThingInService();
}
}
Folosind
Client client = new Client();
client.setService(new Service());
client.doSomeThingInClient();
Mod 3) Interfata de injecție
Verifica https://en.wikipedia.org/wiki/Dependency_injection
===
Acum, acest cod este deja urmărit de Injectare Dependență și este mai ușor pentru a testa "Client" de clasă.
Cu toate acestea, noi încă mai folosesc noi Service()
timp de multe și nu e bine când schimba "Serviciu" constructorul. Pentru a preveni aceasta, putem folosi DI injector, ca
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(...));
}
}
Folosind
Service service = Injector.provideService();
****Avantaje
Dezavantaje
https://en.wikipedia.org/wiki/Dependency_injection
O dependența este un obiect care poate fi folosit ("Serviciu") O injecție este trecerea de dependență ("Serviciu") de la un obiect dependente ("Client"), care ar folosi
Cea mai bună analogie mă pot gândi este chirurg și asistentul său(e) într-un teatru de operații, în cazul în care chirurgul este principala persoană și asistentul său, care oferă diverse chirurgicale componente atunci când el are nevoie de ea, astfel încât chirurgul poate concentra pe un singur lucru face el cel mai bine (chirurgie). Fără asistent chirurg are pentru a obține componente se de fiecare dată că are nevoie de unul.
DI pentru scurt, este o tehnica de a elimina o comună responsabilitate suplimentară (sarcina) pe componente pentru a aduce dependente componente, oferindu-le la ea.
DI te aduce mai aproape de Unică Responsabilitate (SR) principiu, ca un chirurg care se poate concentra pe operatie`.
Când să utilizați DI : mi-ar recomanda folosind DI în aproape toată producția de proiecte ( mici/mari), în special în continuă schimbare de afaceri medii :)
De ce : Pentru că vreau codul pentru a fi ușor testabile, mockable etc, astfel încât să puteți testa rapid modificările și împingeți-l la piață. În afară de ce nu ar fi atunci când există o mulțime de minunat instrumente gratuite/cadre pentru a vă sprijini în călătoria dumneavoastră la un codebase în cazul în care aveți mai mult control.
Aceasta înseamnă că obiectele trebuie să aibă cât mai multe dependențe cum este necesar pentru a face treaba lor și dependențele ar trebui să fie puține. În plus, un obiect dependențele ar trebui să fie pe interfețe și nu pe "beton" obiecte, atunci când este posibil. (Un obiect concret este orice obiect creat cu cuvinte cheie noi.) Cuplarea promovează o mai mare capacitate de reutilizare, mai ușor mentenabilitatea, și vă permite de a oferi cu ușurință "bate joc" de obiecte în loc de servicii costisitoare.
"De Injectare Dependență" (DI) este, de asemenea, cunoscut sub numele de "Inversiune de Control" (Cio), poate fi folosit ca o tehnică pentru a încuraja acest cuplaj slab.
Există două abordări primar pentru punere în aplicare a DI:
E tehnica de trecere obiecte dependențele sale de constructor.
Rețineți că constructorul acceptă o interfață și nu obiect concret. De asemenea, rețineți că o excepție este aruncată dacă orderDao parametru este null. Acest lucru subliniază importanța de a primi o adresă validă de dependență. Constructor de Injecție este, în opinia mea, de preferat mecanism pentru a da un obiect dependențele sale. Este clar pentru dezvoltator, în timp ce invocarea obiect care dependențe trebuie să fie dat la "Persoană" obiect pentru executarea corespunzătoare.
Dar luați în considerare următorul exemplu... sa Zicem ca ai o clasa cu zece metode care nu au dependențe, dar adăugați o nouă metodă care are o dependență pe IDAO. Ai putea schimba constructor pentru a utiliza Constructor de Injecție, dar acest lucru poate forța să modificărilor la toate constructor apeluri de peste tot. Alternativ, ai putea doar să adăugați un nou constructor care ia dependență, dar atunci cum se face un dezvoltator cu ușurință știu când să folosească un constructor de-a lungul altor. În cele din urmă, dacă dependența este foarte scump pentru a crea, de ce ar trebui să fie creat și a trecut la constructor, atunci când acesta poate fi utilizat numai rar? "Setter Injectare" este o altă DI tehnici care pot fi utilizate în astfel de situații.
Setter Injecție nu forța dependențe de a fi trecut la constructor. În schimb, dependențele sunt stabilite pe proprietăți publice expuse de obiect în nevoie. Așa cum rezultă anterior, principala sa motivație pentru a face acest lucru includ:
Aici este un exemplu de cum codul de mai sus va arata asa:
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;
Cred că din moment ce toată lumea a scris pentru DI, permiteți-mi să vă pun câteva întrebări..
Aceasta se bazează pe răspunsul @Adam N-a postat.
De ce nu PersonService nu mai trebuie să vă faceți griji despre GroupMembershipService? Tocmai ați menționat GroupMembership are mai multe lucruri(obiecte/properties) depinde. Dacă GMService a fost cerută în PService, ai'd-l aibă ca o proprietate. Îți poți bate joc de asta indiferent dacă ai injectat sau nu. Singura dată când m-am'd place să fie injectat este dacă GMService avut mai specifice copilului clase, care nu m't știu până la execuție. Atunci'd vreau să vă injectați subclasa. Sau, dacă ai vrut pentru a folosi asta ca nici singleton sau prototip. Pentru a fi sincer, fișierul de configurare are tot ce hardcoded ca ceea ce subclasa pentru un tip (interfață) este injectată în timpul compilării.
EDIT
Un comentariu frumos de Jose Maria Arranz pe DI
DI crește coeziunea prin eliminarea oricăror nevoie pentru a determina direcția de dependență și de a scrie orice lipici cod.
False. Direcția de dependențe este în XML sau sub formă de adnotări, dependențele tale sunt scrise fel de cod XML și adnotări. XML și adnotări SUNT codul sursă.
DI reduce cuplare de a face toate de componente modulare (de exemplu replacable) și au interfețe bine definite pentru fiecare alte.
False. Nu ai nevoie de o DI-cadru pentru a construi un cod modular bazat pe interfețe.
Despre înlocuibile: cu o foarte simplu .proprietăți de arhivă și de Clasă.forName puteți defini clase care se pot schimba. Dacă ORICE clasă de cod poate fi schimbat, Java nu este pentru tine, utilizați un limbaj de scripting. Apropo: adnotări nu poate fi schimbat fără recompilarea.
În opinia mea, nu există un singur motiv pentru cadrele DI: cazan placa de reducere. Cu un bine făcut fabrica de sistem puteți face același lucru, mai controlat și mai previzibil ca dumneavoastră preferat de DI-cadru, DI cadrele promit cod de reducere (XML și adnotări sunt codul sursă prea). Problema este ca acest cazan placa de reducere este doar foarte foarte foarte cazurile simple (un singur exemplu-pe clasă și similare), uneori în lumea reală cules însușit service object nu este la fel de ușor ca de cartografiere o clasă pentru un obiect singleton.
Injectare dependență înseamnă o cale (de fapt orice-fel) pentru o parte de cod (e.g o clasă) de a avea acces la dependinte (alte părți de cod, e.g alte clase, depinde), într-un mod modular, fără a le fi hardcoded (deci se pot schimba sau pot fi overriden liber, sau chiar să fie încărcate într-un alt moment, după cum este necesar)
(și ps , da a devenit o supra-hyped 25$ nume pentru o destul de simplu, concept), mi .25
cenți
Populare răspunsuri sunt nefolositoare, pentru că ele definesc dependența de injecție într-un mod care nu - 't utile. Las's de acord cu faptul ca "dependență" ne referim la unele pre-existente alt obiect care noștri obiect X are nevoie. Dar nu ne't spune că ne-am're face "de injectare dependență" atunci când spunem
$foo = Foo->new($bar);
Spunem că transmiterea parametrilor în constructor. Am'am făcut asta în mod regulat încă de când constructorii s-au inventat.
"de injectare Dependență" este considerat un tip de "inversiune de control", ceea ce înseamnă că unele logica este scos din apelantului. Asta e't cazul când apelantul trece în parametri, astfel încât în cazul în care au fost DI, DI nu ar implica inversiune de control.
DI înseamnă că există un nivel intermediar între apelant și constructorul care gestionează dependențe. Un Makefile este un exemplu simplu de injectare dependență. "apelantului" este persoana tastați "face bar" pe linia de comandă, și "constructor" este compilator. Makefile precizează că, bar depinde de foo, și nu o
gcc -c foo.cpp; gcc -c bar.cpp
înainte de a face o
gcc foo.o bar.o -o bar
Persoana tastați "face bar" nu't trebuie să știți că, bar depinde de foo. Dependența a fost injectat între "face bar" și gcc.
Scopul principal al nivel intermediar nu este doar de a trece în dependențele de la constructor, dar pentru a lista toate dependențele în doar un singur loc, și pentru a le ascunde de coder (nu pentru a face coder le ofere).
De obicei, nivelul intermediar oferă fabrici pentru obiecte construite, care trebuie să furnizeze un rol pe care fiecare a solicitat tip de obiect trebuie să le satisfacă. Ca's, deoarece avand un nivel intermediar, care ascunde detaliile de construcție, tu'am deja suportate de abstractizare pedeapsa impusă de fabrici, deci s-ar putea folosi, precum și în fabrici.