Cum să creați un container de injecție pentru dependență

  • 26.02.16 09:01 •
  • sfedosimov •
  • # 278049
  • Habrahabr •
  • Traducere •
  • 0 •
  • 12300

- la fel ca Forbes, doar mai bine.

Buna ziua tuturor!
Aceasta este o traducere gratuită a articolului Cum să vă construiți propriul container de injectare a dependenței.






pentru că aceasta este prima mea traducere pentru hubra, vă rog să indicați greșeli, inexactități.

Cum să vă creați propriul Container de Injectare a Dependenței.


Căutarea "containerului de injecție a dependenței" de pe ambalator produce în prezent mai mult de 95 de pagini cu rezultate. Cu încredere, putem spune că această "roată" a fost deja inventată.

Cu toate acestea, niciunul bucătar nu a studiat să gătească numai preparate alimentare. De asemenea, niciun dezvoltator nu a învățat vreodată să programeze folosind doar codul gata.

În acest articol, vom învăța cum să facem un simplu pachet de containere pentru injecția de dependență. Toate codurile scrise în articol plus adnotările PHPDoc și testele unitare cu acoperire de 100% sunt disponibile pe GitHub. Toate acestea sunt adăugate și ambalatorului.

Planificăm containerul nostru pentru injectarea dependenței


Să începem prin a planifica ceea ce dorim ca containerul nostru să facă.
Un bun început este de a împărți "Containerul de injecție a dependenței" în două roluri - "Dependența de injecție" și "Container".

Cele două metode cele mai comune pentru efectuarea injecției de dependență sunt prin injectarea constructorului sau prin injectarea unui setter. Acesta este transferul unei clase de dependență printr-un constructor ca argument sau apel de metodă. Dacă containerul nostru va putea să creeze o instanță și să conțină servicii, va fi capabil să implementeze ambele metode.

Pentru a fi un container, acesta trebuie să poată stoca și recupera instanțe de servicii. Aceasta este o sarcină destul de trivială în comparație cu crearea unui serviciu, dar necesită încă o analiză. Pachetul container-interop furnizează un set de interfețe pe care un container le poate implementa. Interfața principală este ContainerInterface. care definește două metode - una pentru primirea serviciului, cealaltă pentru a verifica dacă este definit un serviciu.

Experiența altor containere de injectare a dependenței


Symfony Dependency Injection Container ne permite să definim serviciile în diferite moduri. În YAML. configurația pentru container ar putea arăta astfel:

În PHP, aceeași configurație pentru componenta Symfony Dependency Injection ar arăta astfel:

Noțiuni de bază


Primul lucru pe care trebuie să-l facem este să creați un director și un fișier numit composer.json. care va folosi compozitorul. că autoloaderul clasei noastre a lucrat. Întregul fișier este în prezent o hartă SitePoint \ Container a spațiului de nume în directorul src.


Mai mult, așa cum vom face, containerul nostru va implementa interfața container-interop. avem nevoie de compozitor pentru a le încărca și a adăuga un fișier compozitorului.json:


Împreună cu interfața principală a ContainerInterface. Pachetul container-interop definește de asemenea două interfețe pentru excepții. Primul pentru excepții generale la crearea unui serviciu și celălalt pentru excepții atunci când serviciul solicitat nu a fost găsit. De asemenea, vom adăuga o altă excepție la această listă, pentru acele situații în care parametrul solicitat nu a fost găsit.

Deoarece nu trebuie să adăugăm nicio funcție care să depășească clasa propusă de PHP Exception kernel. aceste clase sunt destul de simple. Deși pot părea inutile, această împărțire face mai ușor pentru noi să le prindem și să le procesăm separat.

Creați folderul src și aveți în el următoarele fișiere src / Exception / ContainerException.php. src / Exception / ServiceNotFoundException.php și src / Exception / ParameterNotFoundException.php respectiv:







Legăturile din container


Clasa Simfoniei Referință. considerată anterior, a permis bibliotecii să distingă valorile PHP pentru utilizarea imediată și argumentele care trebuie înlocuite cu alte servicii din container.

Să împrumutăm această idee și să creăm două clase pentru referințe la parametri și servicii. Deoarece ambele clase vor stoca valorile obiectului pur și simplu de numele resursei la care se referă, este logic să folosiți clasa abstractă de bază. Deci nu trebuie să scriem același cod de două ori.

Creați următoarele fișiere src / Reference / AbstractReference.php. src / Reference / ServiceReference.php și src / Reference / ParameterReference.php, respectiv:

Clasa de containere


E timpul să ne creăm containerul. Vom începe cu schema de bază a clasei noastre de containere și vom adăuga metode la ea pe măsură ce mergem.

Ideea de bază este să luăm două machete în constructorul containerului nostru. Prima matrice trebuie să conțină definițiile serviciului și cea de-a doua definire a parametrilor.

În src / Container.php, puneți următorul cod:


Tot ce am făcut aici a fost implementarea interfeței ContainerInterface din container-interop și încărcarea definițiilor în proprietăți care pot fi accesate mai târziu. De asemenea, am creat proprietatea serviceStore și l-am inițializat cu o matrice goală. Atunci când containerul este rugat să creeze servicii, le stocăm în acest tablou, astfel încât acestea să poată fi restaurate mai târziu, fără a trebui să le recreeze.

Acum, să începem să scriem metode declarate în container-interop. Să începem cu obținerea ($ name). adăugați următoarea metodă la clasă:


Asigurați-vă că adăugați o utilizare la începutul fișierului. Metoda de obținere ($ name) este o verificare simplă pentru prezența unei definiții a serviciului în container. Dacă nu este, va fi aruncat ServiceNotFoundException pe care l-am creat mai devreme. Dacă este, o returnează. Creează și stochează în depozit dacă nu a fost deja creat.

În timp ce suntem în el, trebuie să creați o metodă de preluare a parametrilor din container. Prin acceptarea parametrilor care sunt dedicați constructorului din matricea asociativă n-dimensională, avem nevoie de o modalitate de a accesa cu ușurință orice element din matrice utilizând un singur șir. Modul ușor de făcut este să utilizați punctul ca delimitator, astfel încât linia foo.bar se referă la cheia de bara din tasta foo de la rădăcina matricei de parametri.


Acum am folosit câteva metode pe care nu le-am scris încă. Primul dintre ele are ($ name). care este declarat în container-interop. Aceasta este o metodă destul de simplă și trebuie doar să fie verificată dacă seria de definiții furnizate constructorului conține serviciul de nume $.


O altă metodă pe care trebuie să o scriem este createService ($ name). Această metodă va utiliza definițiile furnizate pentru a crea serviciul. Deoarece nu vrem ca această metodă să fie chemată din afara containerului, o vom face privată.

Primul lucru pe care trebuie să-l faceți este să faceți niște controale rezonabile.
Pentru fiecare serviciu declarat, avem nevoie de o matrice care conține cheia de clasă și cheile opționale de argumente și de apeluri. Acestea vor fi folosite pentru a implementa constructorul și respectiv setter-ul. De asemenea, putem adăuga protecție de la buclele de legătură, verificând dacă am încercat deja să creăm un serviciu.

Dacă argumentul argumentelor există, vrem să convertim acea matrice de definiții de argument într-o serie de valori PHP care pot fi transmise constructorului. Pentru a face acest lucru, trebuie să convertim directorul obiectelor pe care le-am declarat anterior la valorile menționate în container. În acest moment, vom folosi această logică în metoda resolveArguments ($ name, array $ argumentDefinitons).
Folosim metoda ReflectionClass :: newInstanceArgs () pentru a crea un serviciu folosind matricea de argumente. Aceasta este implementarea constructorului.

Dacă există cheia de apelare, dorim să folosim matricea definițiilor apelurilor și să o aplicăm serviciului pe care tocmai l-am creat. Din nou, vom folosi logica într-o metodă separată, definită ca initializeService ($ service, $ name, array $ callDefinitions). Aceasta este punerea în aplicare a setter.


Ultima metodă realizează implementarea setterului într-o instanță a obiectului de serviciu. Pentru a face acest lucru, trebuie să treceți prin mulțimea de definiții atunci când apelați metoda. Cheia metodei este utilizată pentru a specifica metoda, iar tasta opțională pentru argumente poate fi utilizată pentru a furniza argumente pentru această metodă. Putem folosi metoda pe care tocmai am scris-o pentru a traduce argumentele sale în valorile PHP.


Și acum avem un container de injecție pentru dependență. Pentru a vedea exemple de utilizare, verificați depozitul de pe GitHub.

concluzie


Am învățat cum să creăm un container simplu de injecție de dependență. Dar există o mulțime de containere cu caracteristici remarcabile pe care nu le avem încă.

Unele containere de injecție de dependență, cum ar fi PHP-DI și Aura.DI, oferă o funcție numită conexiune automată. Aici, containerul presupune care servicii din container ar trebui să fie implementate în altele. Pentru a face acest lucru, ei folosesc API-ul de reflecție pentru a găsi informații despre parametrii constructorului.

Numai utilizatorii înregistrați pot participa la sondaj. Conectați-vă. te rog.







Articole similare

Trimiteți-le prietenilor: