luni, 13 octombrie 2008

Bijection

Considerata de multi cea mai tare chestie in programare in ultimii ani - desi nu e deloc inventata in ultimii ani :) - de la "slice bread", Dependency Injection a revigorat peisajul aplicatiilor web in Java, dupa nefericitele experiente cu EJB. Pentru ca in loc de umflatele EJB-uri si containerele aferente au aparut frameworkuri "lean and mean", cel mai pregnant exemplu fiind Spring, care lucreaza cu POJO-uri si simplifica semnificativ dezvoltarea, configurarea, deploymentul si mentenanta unei aplicatii. Containerele generice pentru DI (sau Inversion of Control cum mai e numita citeodata, desi nu foarte corect), ca Pico Container, se bucura si ele de mare succes contribuind la raspindirea patternului in aplicatiile non-web. Mai nou Google Guice este un nou container de IoC care aduce citeva lucruri noi, dar, in opinia mea, este doar o expresie a politicii Google de extindere in toate domeniile dezvoltarii de software, un efort al carei ultime iteratii este cred Google Chrome.

Si o paranteza: ca sa fim corecti Dependency Injection este folosita intens si de stack-ul clasic J2EE; in EJB si JSF, de exemplu.

Principiul de baza este destul de simplu, asa numitul Hollywood Principle: "Don't call us, we'll call you!" - chestie spusa se pare de producatori de la Hollywod actorilor aspiranti la un rol. In programare s-ar traduce cam asa: cind un component are nevoie de un serviciu, de la un alt component sau de la frameworkul in care ruleaza, aceste servicii sint "injectate" in componentul respectiv. Cam asta ar fi Inversion of Control: componentele care ofera servicii sint trimise, ca parametrii in constructor sau prin metode set, componentelor care necesita aceste servicii. Binenteles asta se poate face "manual" sau sub controlul unui container, caz in care obiectele sint create de acesta si oferite componentelor care le necesita de catre acesta. Astfel, un component se poate concentra asupra functionalitatii lui specifice, crearea si managementul obiectelor care ofera serviciile necesare fiind efectuata de catre container.
Configurarea obiectelor al carui management e asigurat de container a fost asigurat intii de fisiere xml, actualmente utilizindu-se extensiv anotarile. (Daca asta e mai bine sau mai rau e scopul altei discutii :)

Recent, odata cu introducerea Seam, a aparut un nou concept: dependency bijection. Dupa cum sugereaza numele, bijection face cam acelasi lucru ca dependency injection dar in ambele sensuri. Adica nu numai ca aduce servicii catre component, dar si exporta starea unui component, de obicei catre un context. In acest fel se creaza o legatura bijectiva (matematica sa traiasca!) intre component si container, transfer de stare bidirectional. De fapt Seam este despre stare si context, asa ca un astfel de concept isi avea rostul perfect aici. Am putea spune deci ca Hollywood Principle a fost extins putin si in avantajul actorilor: "Don't call us, we'll call you!", dar dupa ce obtine rolul actorul poate spune si el "I'll call you, if I will take the part!"
Deci, un component primeste obiectele si/sau serviciile de care are nevoie pentru a-si indeplini functionalitatea de la frameworkul in care ruleaza, apoi isi "outjecteaza" (DEX-ul sa ma ierte!) starea catre framework, mai precis catre contextul in care exista.
Seam introduce notiuni mai complexe, si mai adecvate, de context decit aplicatiile tipice J2EE, incercind, si din cite stiu reusind intr-o buna masura, sa acopere nevoile resimtite in dezvoltare. Sint 7 tipuri de context disponibile in Seam: stateless (practic un non-context), event (la nivelul unui request), page (starea este mentinuta la nivelul paginii, disponibila pentru toate eventurile generate intr-o anumita pagina), conversation (default se intinde pe 2 pagini, dar se poate extinde peste oricite request-uri demarcate cu anotatii Seam), session (la nivelul sesiunii J2EE, stare asociata cu userul), business process (stare asociate cu procese async care ruleaza pe timp indelungat; din cite stiu - deocamdata - functioneaza doar cu jBoss jBPM; starea este mentinuta pe durata procesului pentru multiple interactii de la useri diferiti), application (la fel ca J2EE application context).
Din punctul meu de vedere, contextele conversation si business process sint atit elementul de noutate cit si partile cele mai interesante. Mai ales, business process context, aici dependency bijection este si foarte utila si foarte convenabila, simplificind managementul starii intre procesele de lunga durata si operatiile online.

Bijection se poate folosi pentru orice obiect declarat ca obiect Seam (@Name). Seam intercepteaza orice este marcat cu @In (dependency injection) sau @Out (dependency outjection); anotarile se pot folosi si impreuna: primesc un obiect, il modific si il trimit inapoi in context.
De notat este si ca in Seam bijection se face la invocare: unui component care foloseste bijection pentru un obiect Seam, nu i se injecteaza acest obiect la momentul crearii ci in momentul apelului unei metode care il foloseste; la finalul metodei se face outject.
BTW, Seam face toata demonstratia asta de magie folosind interceptori J2EE, nu e nimic picat din cer.

Pe masura ce o sa mai imi bag nasul in Seam, sper sa continui sa scriu mai multe despre acest framework. In fond este facut de Gavin King, si stiu, de la Hibernate incoace, ca e bine sa asculti cind Gavin are ceva de zis :) Mai ales dupa ce JPA a fost creat "dupa chipul si asemanarea" Hibernate :D Iar acum se lucreaza intens la JSR-299 WebBeans. Ghici cu cine seamana? :)