Friday, 30 January 2009

„Dacă vrei să mergi repede, mergi încet”, sau „Cum am căpătat eu propria perspectivă asupra DI”

Trebuia la un moment dat să găsesc o soluţie bună pentru implementarea Dependency Injection într-un proiect. Ştiam că Spring conţine o implementare OK, dar nu voiam să aduc Spring în proiect doar pentru asta. Studiind tot felul de variante, majoritatea prost documentate şi doar în fază de dezvoltare, inclusiv NanoContainer şi PicoContainer, am pierdut mult timp. Ajunsesem aproape de deadline şi nu găsisem o soluţie. Atunci m-am oprit şi am stat o oră să mă uit la acest film. A fost prima mea experienţa de lucru cu o bibliotecă de cod scris de Google şi a fost una excelentă. Mi-a deschis ochii şi mi-a risipit graba şi stressul. Am văzut astfel ce pot face nişte oameni inteligenţi: o piesă de software gândită şi proiectată excelent (nu, Google nu-mi dă bani pentru asta). Foloseşte toate facilităţile date de limbajul Java în versiunile de la Tiger încoace exact aşa cum ar trebui folosite.

De asemenea, am căpătat propria înţelegere a ceea ce înseamnă DI.

În Java, totul trebuie să fie gândit în termeni de obiecte. În sensul ăsta, limbajul Java a fost mult prea avansat pentru mulţi care au învăţat să-l folosească. Pentru începători (şi toţi programatorii Java din anii de început ai limbajului erau începători), tentaţia de a repeta şabloane de programare procedurală este mare. Ea se manifestă prin cod mult scris în metode statice, şi prin diferite şabloane de proiectare care abuzează de membri şi metode statice, cum ar fi Singleton sau Factory. Nu spun că aceste şabloane ar fi inutile. Dar în momentul când o aplicaţie are vreo 7 factory-uri şi 20 de singletonuri, înseamnă că ceva este greşit. Singleton este o îmbunătăţire adusă claselor cu metode statice, pentru a permite eventual moştenirea, dar se rămâne tot cu un context static care reţine singura instanţă a unei clase pe care o folosesc mai multe alte obiecte.
Static is evil. Nu permite moştenirea de clase, nu permite suprascrierea de metode, într-o lume în care flexibilitatea trebuie să fie norma. Membrii statici sunt ca nişte variabile globale, cu tot cu efectele lor laterale.

O aplicaţie orientată obiect elegantă are codul plasat în clase obişnuite, ale căror instanţe reţin referinţe către celelalte obiecte, referinţe utilizate în cadrul metodelor. Metodele nu trebuie să apeleze constant la contexte statice pentru a obţine referinţe ale obiectelor de care au nevoie. Dincolo de lipsa de eleganţă, este de o ineficienţă cruntă. Mai bine să gândim relativist, în stil Einstein, scuturându-ne de nevoia ancestrală de absolut (a se citi „static”) provenită din programarea procedurală.

Am ajuns, cu ajutorul Guice, să înţeleg Dependency Injection ca fiind tehnica ce permite „cablarea” obiectelor la crearea lor, prin stabilirea pentru fiecare obiect a referinţelor necesare. Codul arată foarte diferit. Toate legăturile între obiecte sunt descrise simplu, în fiecare clasă fiind definite ca membri referinţe către obiecte din alte clase, cu câteva adnotări care să ghideze frameworkul cum să le cableze. Desigur, şi Guice apelează contextul static, dar o face doar la instanţierea de obiecte, pentru a stabili legăturile. În timpul ciclului de viaţă al obiectelor, metodele claselor lor nu mai au de ce să apeleze contexte statice, deoarece obiectele pe care sunt apelate deja deţin toate referinţele necesare. Ca rezultat, aplicaţia arată curat, fără statice, şi cu toate funcţionalităţile declarate în interfeţe, ceea ce sprijină serios proiectarea curată şi documentarea codului.

Practic, Guice este un Factory configurabil, care creează obiecte şi menţine referinţele la ele pentru a le putea injecta în alte obiecte pe care le creează. După introducerea lui în proiect, am ajuns să abandonez toate celelalte factory-uri scrise manual, cu defecte mai mici sau mai mari, şi să le aşez în contextul elegant al Guice.