Întrebări frecvente despre DI (FAQ)

Este DI un alt nume pentru IoC?

Inversion of Control (IoC) este un principiu axat pe modul în care este executat codul – dacă codul dvs. rulează cod străin sau dacă codul dvs. este integrat în cod străin, care îl apelează ulterior. IoC este un termen larg care include evenimente, așa-numitul Principiu Hollywood și alte aspecte. Parte a acestui concept sunt și factory-urile, despre care vorbește Regula nr. 3: lasă pe seama factory-ului, și care reprezintă o inversiune pentru operatorul new.

Dependency Injection (DI) se concentrează pe modul în care un obiect află despre alt obiect, adică despre dependențele sale. Este un pattern de design care necesită transmiterea explicită a dependențelor între obiecte.

Se poate deci spune că DI este o formă specifică de IoC. Cu toate acestea, nu toate formele de IoC sunt potrivite din punct de vedere al curățeniei codului. De exemplu, printre anti-pattern-uri se numără tehnicile care lucrează cu starea globală sau așa-numitul Service Locator.

Ce este Service Locator?

Este o alternativă la Dependency Injection. Funcționează prin crearea unui depozit central unde sunt înregistrate toate serviciile sau dependențele disponibile. Când un obiect are nevoie de o dependență, o solicită de la Service Locator.

Cu toate acestea, în comparație cu Dependency Injection, pierde din transparență: dependențele nu sunt transmise direct obiectelor și nu sunt la fel de ușor de identificat, ceea ce necesită examinarea codului pentru a descoperi și înțelege toate legăturile. Testarea este, de asemenea, mai complicată, deoarece nu putem transmite pur și simplu obiecte mock obiectelor testate, ci trebuie să trecem prin Service Locator. În plus, Service Locator perturbă designul codului, deoarece obiectele individuale trebuie să știe despre existența sa, ceea ce diferă de Dependency Injection, unde obiectele nu au cunoștință despre containerul DI.

Când este mai bine să nu folosim DI?

Nu sunt cunoscute dificultăți asociate cu utilizarea pattern-ului de design Dependency Injection. Dimpotrivă, obținerea dependențelor din locații disponibile global duce la o întreagă serie de complicații, la fel ca și utilizarea Service Locator-ului. Prin urmare, este recomandat să se utilizeze DI întotdeauna. Aceasta nu este o abordare dogmatică, ci pur și simplu nu a fost găsită o alternativă mai bună.

Cu toate acestea, există anumite situații în care nu transmitem obiecte și le obținem din spațiul global. De exemplu, la depanarea codului, când trebuie să afișați valoarea unei variabile într-un anumit punct al programului, să măsurați durata unei anumite părți a programului sau să înregistrați un mesaj. În astfel de cazuri, când este vorba de acțiuni temporare care vor fi ulterior eliminate din cod, este legitim să se utilizeze un dumper, un cronometru sau un logger disponibil global. Aceste instrumente nu fac parte din designul codului.

Are utilizarea DI dezavantaje?

Implică utilizarea Dependency Injection vreun dezavantaj, cum ar fi o complexitate crescută a scrierii codului sau o performanță redusă? Ce pierdem când începem să scriem cod în conformitate cu DI?

DI nu are impact asupra performanței sau a cerințelor de memorie ale aplicației. Performanța containerului DI poate juca un anumit rol, însă în cazul Nette DI, containerul este compilat în PHP pur, astfel încât overhead-ul său în timpul rulării aplicației este practic nul.

La scrierea codului, este necesar să se creeze constructori care acceptă dependențe. În trecut, acest lucru putea fi anevoios, însă datorită IDE-urilor moderne și promovării proprietăților constructorului, acum este o chestiune de câteva secunde. Factory-urile pot fi generate ușor folosind Nette DI și plugin-ul pentru PhpStorm printr-un clic de mouse. Pe de altă parte, dispare necesitatea de a scrie singleton-uri și puncte de acces statice.

Se poate constata că o aplicație proiectată corect care utilizează DI nu este nici mai scurtă, nici mai lungă în comparație cu o aplicație care utilizează singleton-uri. Părțile de cod care lucrează cu dependențe sunt doar extrase din clasele individuale și mutate în locații noi, adică în containerul DI și în factory-uri.

Cum să rescrii o aplicație legacy la DI?

Trecerea de la o aplicație legacy la Dependency Injection poate fi un proces solicitant, în special pentru aplicații mari și complexe. Este important să abordați acest proces sistematic.

  • La trecerea la Dependency Injection, este important ca toți membrii echipei să înțeleagă principiile și procedurile utilizate.
  • Mai întâi, efectuați o analiză a aplicației existente și identificați componentele cheie și dependențele lor. Creați un plan care să specifice ce părți vor fi refactorizate și în ce ordine.
  • Implementați un container DI sau, și mai bine, utilizați o bibliotecă existentă, de exemplu Nette DI.
  • Refactorizați treptat părțile individuale ale aplicației pentru a utiliza Dependency Injection. Acest lucru poate include modificarea constructorilor sau metodelor astfel încât să accepte dependențe ca parametri.
  • Modificați locurile din cod unde se creează obiecte cu dependențe, astfel încât dependențele să fie injectate de container. Acest lucru poate include utilizarea factory-urilor.

Rețineți că trecerea la Dependency Injection este o investiție în calitatea codului și în mentenabilitatea pe termen lung a aplicației. Deși poate fi dificil să efectuați aceste modificări, rezultatul ar trebui să fie un cod mai curat, mai modular și mai ușor de testat, pregătit pentru extinderi și întreținere viitoare.

De ce se preferă compoziția în locul moștenirii?

Este mai potrivit să se utilizeze compoziția în locul moștenirii, deoarece servește la reutilizarea codului fără a ne preocupa de consecințele modificărilor. Oferă deci o legătură mai slabă, în care nu trebuie să ne temem că modificarea unui cod va necesita modificarea altui cod dependent. Un exemplu tipic este situația denumită constructor hell.

Se poate utiliza Nette DI Container în afara Nette?

Categoric. Nette DI Container face parte din Nette, dar este proiectat ca o bibliotecă independentă care poate fi utilizată independent de celelalte părți ale framework-ului. Este suficient să o instalați folosind Composer, să creați un fișier de configurare cu definiția serviciilor dvs. și apoi, folosind câteva linii de cod PHP, să creați containerul DI. Și puteți începe imediat să beneficiați de avantajele Dependency Injection în proiectele dvs.

Modul concret de utilizare, inclusiv codurile, este descris în capitolul Containerul Nette DI.

De ce este configurația în fișiere NEON?

NEON este un limbaj de configurare simplu și ușor de citit, care a fost dezvoltat în cadrul Nette pentru setarea aplicațiilor, serviciilor și dependențelor lor. În comparație cu JSON sau YAML, oferă opțiuni mult mai intuitive și flexibile în acest scop. În NEON se pot descrie natural legături care în Symfony & YAMLu nu ar putea fi scrise fie deloc, fie doar printr-o descriere complicată.

Nu încetinește aplicația parsarea fișierelor NEON?

Deși fișierele NEON se parsează foarte rapid, acest aspect nu contează deloc. Motivul este că parsarea fișierelor are loc doar o singură dată la prima rulare a aplicației. Apoi se generează codul containerului DI, se salvează pe disc și se rulează la fiecare request ulterior, fără a fi necesară o altă parsare.

Așa funcționează în mediul de producție. În timpul dezvoltării, fișierele NEON se parsează de fiecare dată când conținutul lor se modifică, pentru ca dezvoltatorul să aibă mereu containerul DI actualizat. Parsarea în sine este, așa cum s-a spus, o chestiune de moment.

Cum accesez din clasa mea parametrii din fișierul de configurare?

Să ne amintim Regula nr. 1: lasă-l să ți se transmită. Dacă o clasă necesită informații din fișierul de configurare, nu trebuie să ne gândim cum să ajungem la acele informații, ci pur și simplu le solicităm – de exemplu, prin constructorul clasei. Și realizăm transmiterea în fișierul de configurare.

În acest exemplu, %myParameter% este un substituent pentru valoarea parametrului myParameter, care se transmite constructorului clasei MyClass:

# config.neon
parameters:
	myParameter: Some value

services:
	- MyClass(%myParameter%)

Dacă doriți să transmiteți mai mulți parametri sau să utilizați autowiring, este recomandat să împachetați parametrii într-un obiect.

Suportă Nette PSR-11: Container interface?

Nette DI Container nu suportă PSR-11 direct. Cu toate acestea, dacă aveți nevoie de interoperabilitate între Nette DI Container și biblioteci sau framework-uri care așteaptă PSR-11 Container Interface, puteți crea un adaptor simplu, care va servi ca o punte între Nette DI Container și PSR-11.

versiune: 3.x