Documente Academic
Documente Profesional
Documente Cultură
n domeniul proiectrii software exist soluii reutilizabile pentru problemele ce apar mai des. Un ablon de proiectare nu este un element de proiectare aflat ntr -o form final, direct transformabil n cod, ci doar o soluie pentru o anumit problem, soluie care, n timp, s-a dovedit folositoare n situaii asemntoare. abloanele de proiectare orientate obiect conin clase, relaiile i interaciunile dintre ele. Clasele coninute nu sunt ntr-o form final, structurile acestora putndu-se extinde cu elementele specifice fiecrui proiect. Algoritmii nu sunt considerai ca fiind abloane de proiectare deoarece ei rezolv probleme de implementare. Folosirea abloanelor de proiectare poate duce la creterea att a vitezei de dezvoltare a software-ului, ct i a calitii acestuia, prin utilizarea unor soluii testate, care iau dovedit eficacitatea. Proiectarea software-ului presupune luarea unor decizii a cror corectitudine se dovedete mai trziu, la partea de implementare. Reutilizarea unor abloane de proiectare ajut la prevenirea unor probleme majore i mbuntete claritatea codului pentru programatorii i arhitecii familiari cu aceste abloane. Reutilizarea abloanelor de proiectare este diferit de reutilizarea codului. Reutilizarea abloanelor de proiectare este practic o reutilizare de idei i nu de componente. n domeniul transformrii celor mai utilizate abloane de proiectare n componente se vorbete de o rat de succes de dou treimi (Meyer i Arnout). Nu orice ablon software este un ablon de proiectare. abloanele de proiectare privesc doar uurarea muncii de proiectare a aplicaiilor software. De exemplu, mai exist n domeniul software aa numitele abloane arhitecturale (architectural patterns), ce descriu, aa cum le sugereaz i denumirea, soluiile unor probleme de arhitectur software. abloanele de proiectare pot fi grupate n mai multe categorii: abloane creaionale (creational patterns); o singleton; o builder (constructor); o metod factory; o clas abstract factory; o prototip; abloane structurale (structural patterns); o adaptor; o compozit; o facade; o proxy; abloane de comportament (behavioral patterns) o observer; o strategy; o command; o iterator; o memento; o visitor; o mediator; o lan de responsabiliti (chain of responsability) Pentru exemplificare vom crea n NetBeans dou proiecte: un proiect UML Java platform Model i un proiect Java Desktop Application. Pentru o mai bun organizare a
1
claselor vom crea n ambele proiecte pachetele surs: sabloane_creationale, sabloane_structurale i sabloane_comportamentale. n proiectul UML crem o diagram de clase i dm n spaiul alb al acesteia click dreapta i Apply Design Pattern.... Dac un ablon nu presupune prea multe clase i dorim s introducem ablonul direct n model fr s l vizualizm ntr-o clas de diagrame putem da click dreapta pe nodul Model i vom gsi aceeai opiune Apply Design Pattern.... Wizardul ce se deschide ne d posibilitatea s introducem din proiectul GoF Design Patterns n propriul proiect UML principalele abloane de proiectare din literatura de specialitate (GoF vine de la Gang of Four, autorii Design Patterns: Elements of Reusable Object-Oriented Software, o carte de referin n literatura de specialitate). Clasele proiectului UML pot fi generate automat n proiectul Java de la opiunea Generate Code (click dreapta pe clasa UML).
i ea ne va returna ntotdeuna obiectul unic instaniat (atributul static de acelai tip cu clasa n care este inclus).
Fig. 8.1 Reprezentarea unei clase singleton Instana unic poate fi creat:
1) implicit la definirea acestui element (o instaniere nu tocmai lene, mai degrab grbit i risipitoare), caz n care metoda instance( ) trebuie doar s returneze o referin la acest obiect:
public static Singleton instance() { return uniqueInstance; }
2) explicit n metoda static instance( ), dac o astfel de instan nu exist deja (aceast modalitate de instaniere este denumit instaniere lene).
public static Singleton instance () { if(uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; }
La lansarea codului de mai sus descoperim c, indiferent de ceea ce se spune aceeasiMarie are aceeai plarie (pardon ... aceeai valoare a atributului singletonData) i asta pentru c este aceeai marie (... acelai obiect uniqueInstance). Exemple de utilizare a ablonului singleton: - clasa user ntr-o aplicaie n care se dorete gestiunea unitar a drepturilor unui utilizator logat; - clasa destinat conexiunii cu baza de date atunci cnd se dorete ca aplicaia s foloseasc la un moment dat o singur baz de date. Pot exista i versiuni ale ablonului singleton, cele n care se limiteaz instanierea unei clase nu doar la o instan, ci la un numr finit de instane (de preferat nu numr mic). De exemplu, ntr-un joc de ah clasa juctor ar trebui s fie instaniat numai de dou ori. Soluia n acest caz presupune: - un constructor privat; - dou atribute statice de tipul clasei (uniqueWhite i uniqueBlack); - dou metode statice care s returneze cele dou instane unice (getUniqueWhite i getUniqueBlack).
Fig. 8.2 Clasa singleton Player ntr-un joc de ah Metodele statice din acest exemplu ar trebui s se asigure mai nti c exist instanele statice corespunztoare i apoi s le iniializeze, adic s le dea o nuan de alb sau de negru:
public Player getUniqueWhite() { if (uniqueWhite == null) { uniqueWhite = new Player(); color = "White"; } return uniqueWhite; }
Observaie: n exemplul de mai sus nici nu este nevoie de o operaie setColor, deoarece culoarea este stabilit doar la instanierea juctorului i nemodificat pe parcursul jocului. NetBeans ne d posibiliatea s crem propriile abloane de proiectare pe lng cele prestabilite folosind opiunea Window\Other\UML Design Center. Aici putem crea un nou proiect cu abloane, denumit eventual MyPatterns. n acest proiect putem specifica propriul singleton crend urmtoarea diagram de clase:
Creator = clasa/clasele ce conin metodele Factory ablonul metod factory definete o interfa (Creator) destinat crerii unui obiect, dar las subclasele (ConcreteCreator) s decid (prin suprascriere) care clas (ConcreteProduct) va fi efectiv instaniat.
Fig. 8.5 Exemplu de ablon metod factory ablonul este folosit n ierarhiile de clase paralele, cnd obiectele dintr-o ierarhie creeaz obiecte corespunztoare din cealalt ierarhie. De exemplu, putem dezvolta o ierarhie de psri (gin, ra, stru), ce produc anumite tipuri de ou. Fiecrui element din ierarhia de psri i corespunde un anumit tip de ou (nu punem sraca gin s produc un ou de stru, spre bucuria ei).
n plus fa de codul generat automat pe baza diagramei de mai sus va trebui sa suprascriem metodele creazaOu din clasele Gaina i Strut, n aa fel nct s facem legtura logic dintre cele dou ierarhii de clase.
public final class gaina extends pasare { public final ou creazaOu(){ return new oudegaina(); } }
i
public final class strut extends pasare { public final ou creazaOu(){ return new oudestrut(); } }
n practic are sens s folosim ablonul cnd metodele de mai sus sunt complexe, presupun accesul la caracteristici ale mediul client i calculeaz valori pentru atributele obiectelor obinute. Clientul acestui ablon doar creaz un obiect de tipul unei anumite psri (s spunem gin) i prin utilizarea metodei creazaOu obine un obiect de tipul Oudegaina, dei nu a scris explicit n cod numele acestei clase.
public static void main(String[] args) { creationale.factory.gaina gaina; creationale.factory.ou oudeceva; gaina = new creationale.factory.gaina(); oudeceva = gaina.creazaOu(); System.out.println("A fost creat oudeceva.getClass().getSimpleName()); }
un
"
Rezultatul afiat este: A fost creat un oudegaina Metodele factory pot accepta opional parametri care definesc modul n care obiectul este creat. Astfel putem crea variante simplificate ale acestui ablon, n care utilizam o singur ierarhie de clase, cea a Produselor i o singur clas Creator cu o metod factory a crui parametru s ajute la identificarea tipului concret de produs realizat. Acest ablon poate fi implementat cu dificultate atunci cnd este folosit pentru clase ce au deja clieni. Adic dac ncepem prin aplicaie s folosim constructorul implicit de ou, e puin cam trziu s mai introducem n ecuaie i pasrea cu metoda ei factory de fcut ou (creazaOu).
ablonul abstract factory asigur o modalitate de grupare a mai multor metode factory ce au elemente (teme) comune. Codul client lucreaz doar cu tipul abstract de factory, nedepinznd de tipurile concrete. Conform acestui ablon, sunt create obiecte concrete, dar codul client le acceseaz doar prin interfaa lor abstract. Adugarea de noi tipuri concrete sistemului se face prin modificarea codului client, n aa fel nct s foloseasc o nou clas concret factory.
Fig. 8.6 ablonul clas abstract factory ablonul clas abstract Factory este util atunci cnd o aplicaie trebuie s creeze diferite grupuri de obiecte n funcie de o anumit stare a mediului de execuie. De exemplu, o aplicaie poate fi gndit n aa fel nct s afieze diferite interfee grafice n funcie de mrimea i rezoluia ecranului. Pentru ecrane mici aplicaia poate apela la interfee grafice gndite s ncap ntr-un spaiu astfel limitat. De exemplu, textele din interfa pot fi scrise n mod abreviat i/sau cu font-uri mai mici, iar imaginile afiate ar avea un numr mai mic de pixeli i de culori. Fiecare din aceste interfee grafice urmeaz s implementeze aceeai interfa, adic sunt vizibile din cadrul aplicaiei client n mod identic. n exemplul urmtor clasa abstract InterfataGrafica are rolul de Factory i ajut la crearea de obiecte (imagine, text, liste etc.) de diferite tipuri optimizate pentru diferite ecrane. Pentru fiecare grup de ecrane se creaz clase concrete Factory: InterfataGraficaMica, InterfataGraficaMedie, InterfataGraficaMare. n aplicaia ce va folosi una din aceste interfee putem scrie:
Toolkit toolkit = Toolkit.getDefaultToolkit(); Dimension scrnsize = toolkit.getScreenSize(); int resolution = toolkit.getScreenResolution(); System.out.println ("Screen width: " + scrnsize.getWidth()); System.out.println ("Screen height: " + scrnsize.getHeight()); System.out.println ("Screen resolution: " + resolution); InterfataGrafica myGUI = null; if (resolution < 40) {myGUI = new InterfataGraficaMica();} if (resolution >= 40 && resolution<96) {myGUI = new InterfataGraficaMedie();} if (resolution >= 96) {myGUI = new InterfataGraficaMare();} System.out.println("Se foloseste o " + myGUI.getClass().getSimpleName());
8.1.4 Prototip
ablonul const n folosirea unei instane drept prototip pentru crearea (clonarea) de noi obiecte. ablonul protoype este folosit pentru: - a evita folosirea subclaselor concrete factory n aplicaiile client; - a evita crearea de obiecte n mod clasic, cu metode constructor. Pentru implementarea acestui ablon se declar o clas de baz abstract ce specific o operaie clonePrototype. Orice clas ce are nevoie de un constructor polimorfic va deriva din clasa de baz abstract i implementeaz operaia clonePrototype.
Fig. 8.7 ablonul prototip Operaia clonePrototype poate fi folosit dup crearea unei prime instane a clasei respective. A clona un obiect nseamn s se creeze un nou obiect i s i se modifice acestuia valorile atributelor cu valorile atributelor obiectului clonat. Avantajele ablonului se vd atunci cnd realizarea acestei prime instane presupune un efort deosebit de timp sau resurse, efort ce nu mai este necesar de la a doua instan. Presupunnd clonarea oiei Dolly cel mai mnemonic exemplu despre clonare:
Utilizarea ablonului de mai sus presupune implementarea metodei clonePrototype din clasa Sheep:
public Prototype clonePrototype () { Sheep clonedSheep = new Sheep(); clonedSheep.rasa = this.rasa; clonedSheep.culoare = this.culoare; clonedSheep.varsta = this.varsta; return clonedSheep; }
8.1.5 Builder
ablonul builder ajut la abstractizarea pailor construirii unor obiecte, altfel spus a reetelor de fabricaie. Obiectele de obinut nu fac parte dintr-o anumit ierarhie de clase, dar exist constrngerea ca valorile atributelor acestora s nu aib sens dect ntr-o anumit combinaie. Fiecare combinaie de valori ale atributelor se obine cu un anumit builder (cu o anumit reet de fabricaie). Aceti builder-i sunt organizai ntr-o ierarhie de clase.
Fig. 8.9 ablonul builder Clasa director este cea care construiete efectiv produsul cu ajutorul unui builder (reete de fabricaie) pe care l conine. Metoda construct din clasa director include o secven
10
de apelri a metodelor de construire a prilor (atributelor) produselor. Cum toate produsele (obiectele) obinute sunt instane ale aceeleiai clase nseamn c toate au aceleai pri i prin urmare toate se pot obine prin respectarea aceleiai secvene de construire. Pot exista variante ale acestui ablon n care Builder este o clas abstract i nu o interfa. Motivul folosirii unei clase abstracte poate fi n acest caz agregarea n Builder a unui obiect produs protejat (protected). Cel mai reuit exemplu de utilizare a ablonului builder este cel de pe wikipedia unde clasa cu rolul product este clasa pizza, clasele cu rolurile concretebuilder sunt diferitele reete de pizza, iar clasa cu rolul director este clasa buctar (exemplul de pe wikipedia scris in C# este corect, cel n Java are lacune).
Fig. 8.10 Exemplu de ablon builder n exemplul de mai sus, clasele reet i pizza pot rmne cu codul generat automat pe baza diagramei, dar: - clasa buctar trebuie s tie ordinea n care se pregtesc diferitele pri de pizza; - clasa RetetaPizzaSalami trebuie s tie cum se pregtete fiecare parte din pizza salami.
public class Bucatar { public Pizza gateste (Reteta oRetetaDePizza) { oRetetaDePizza.pregatesteBlat(); oRetetaDePizza.pregatesteSos(); oRetetaDePizza.pregatesteContinut(); return oRetetaDePizza.getPizza(); }}
11
public class RetetaPizzaSalami extends Reteta { public void pregatesteBlat () { pizza.setBlat("subtire"); } public void pregatesteSos () { pizza.setSos("iute"); } public void pregatesteContinut () { pizza.setContinut("salam"); } }
n aplicaia client ajunge s i apelm unui buctar metoda gtete i s i spunem ce fel de pizza dorim, fr s cunoatem detaliile reetei de fabricaie.
sabloane_creationale.Bucatar popescu = new sabloane_creationale.Bucatar(); sabloane_creationale.RetetaPizzaSalami retetaPizzaSalami = new sabloane_creationale.RetetaPizzaSalami(); sabloane_creationale.Pizza pizza = popescu.gateste(retetaPizzaSalami); System.out.println("Pizza obtinuta contine "+ pizza.getContinut() + ", are blatul " + pizza.getBlat()+ " si sosul " + pizza.getSos() );
Avantajul acestui ablon const n separarea reetei de fabricaie de beneficiarul produselor obinute.
12
Fig. 8.11 ablonul obiect adaptor n diagrama de mai sus o clas Client tie s foloseasc doar clase ce implementeaz interfaa Target. Toat logica scris n clasa Client depinde de aceast interfa. Ce ne facem dac la un moment dat clasa Client trebuie s foloseasc i clase care nu au implementat interfaa Target, dar au aceeai funcionalitate dorit de clasa Client (aa cum este clasa Furnizor)? Mai sus am scris ngroat cuvntul i pentru a arta c rescrierea clasei Client este exclus, ea trebuind s poat funciona i cu vechile implementri ale interfeei Target. Soluia propus de ablonul obiect adaptor este de a crea o clas Adapter care: - s conin un atribut de tipul clasei de adaptat, clasa Furnizor; - s implementeze interfaa Target;
13
- s suprascrie metodele interfeei Target (cum ar fi metoda request), n aa fel nct la execuia acestora s se apeleze metodele instanei de tip Furnizor (de exemplu, metoda specificRequest). n metoda request a adaptorului se scrie n acest caz:
public void request () { furnizor.specificRequest(); }
Un exemplu de utilizare a ablonului object adaptor este cel n care aplicaiile client de messenger pot utiliza diferite servere de messenger. Iniial un client de messenger poate fi dezvoltat pentru un singur server, ulterior dorindu-se utilizarea i a altor servere. Figura 8.12 face referire la clasele specifice unei astfel de aplicaii iniiale n care clientul, n metoda sendText, utilizeaz metoda sendText dintr -un anumit server.
Fig. 8.12 Diagrama claselor ntr-o aplicaie de messenger Pentru utilizarea unui alt server de messenger, clasa ServerMessenger se poate specializa ntr-un adaptor ce conine ca i membru un obiect de tipul unui alt server. Metoda sendText a adaptorului suprascrie metoda sendText a serverului iniial, delegnd activitatea de realizat membrului obiect de tipul celuilalt server, mai exact folosind metoda sendMessage a acestuia.
Un alt exemplu de utilizare a ablonului obiect adaptor este cel dintr-un sistem B2B n care aplicaia de achiziie a unui magazin tie s foloseasc iniial doar o anumit aplicaie de defacere instalat la un furnizor. n aceast form iniial a sistemului exist doar elementele din stnga ale urmtoarei diagrame:
Fig. 8.14 Aplicaie B2B cu un object adapter Clasa Achizitie depinde de interfaa IDesfacere mcar prin faptul c metoda cumpar apeleaz metoda vinde. Dac la un moment dat apare un Furnizor care are instalat o alt aplicaie, ce nu implementeaz interfaa IDesfacere, dar ajut tot la vnzarea de marf, putem crea clasa AdapteazaSale. Conform acestui ablon clasa AdapteazaSale: - conine un atribut de tipul clasei de adaptat, clasa Sale; - implementeaz interfaa IDesfacere; - suprascrie metoda vinde, n aa fel nct aceasta s apeleze metodele setCurrentProduct i sell ale obiectului de tip Sale. La codul generat pe baza diagramei anterioare am adugat un cod formatat aici cu litere ngroate pentru a nelege ce mai rmne de scris la mn din prezentul ablon.
public class Achizitie { private IDesfacere mIDesfacere; public void cumpara (String codProdus, Double cantitate, String furnizor) { //in functie de furnizor folosim anumite implementari if (furnizor.equals("furnizor1")) mIDesfacere = new Desfacere(); if (furnizor.equals("furnizor2")) mIDesfacere = new AdapteazaSale(); mIDesfacere.vinde(codProdus, cantitate); } } public interface IDesfacere { public void vinde (String codProdus, Double cantitate); }
15
public class Desfacere implements IDesfacere { public void vinde (String codProdus, Double cantitate) { System.out.println("Se foloseste clasa Desfacere pentru a cumpara produsul "+ codProdus); } } public class AdapteazaSale implements IDesfacere { private Sale mSale = new Sale(); public void vinde (String codProdus, Double cantitate) { int quantity = cantitate.intValue();//adaptam tipul parametrilor mSale.setCurrentProduct(codProdus);//lansam doua metode in loc de una mSale.sell(quantity); } } public class Sale { private String currentProduct; public void sell (int quantity) { System.out.println("Se foloseste clasa Sale pentru a cumpara produsul " + this.currentProduct); } public String getCurrentProduct () { return currentProduct; } public void setCurrentProduct (String val) { this.currentProduct = val; } }
16
Fig. 8.15 ablonul class adapter cu implementare multipl de interfee Adaptorul descris n figura 8.15 poate fi folosit att pentru o aplicaie ce depinde de ClassA, dar care dispune de ClassB, ct i invers. ablonul class adapter presupune folosirea motenirii multiple, de unde i vin i anumite dezavantaje: - nu toate mediile de programare suport complet motenirea multipl (de aceea figura 8.15 face referire la implementrile a dou interfee i nu la extinderea a dou clase); - pot apare conflicte ntre operaiile celor dou interfee cnd aceste operaii au aceeai semntur dar o semantic diferit. n figura 8.16 exemplificm o clas Adaptor ce implementeaz trei interfee: IDesfacere, ISale, IVente. Practic lum problema de la ultimul exemplu de object adapter (cea cu achiziiile ntr-un sistem B2B) i o rezolvm cu un class adapter, pentru a nelege mai bine diferenele dintre acestea. Clasa cu rolul de Client (clasa Achizitie) nu mai conine un membru ce implementeaz interfaa IDesfacere, ci conine un membru de tip Adapter, adic structura acesteia este pregtit din prima s utilizeze diferite sisteme de desfacere.
17
if (furnizor.equals("furnizor1")) mAdaptor.setDesfacere(new Desfacere()); if (furnizor.equals("furnizor2")) mAdaptor.setSale(new Sale()); if (furnizor.equals("furnizor3")) mAdaptor.setVente(new Vente()); mAdaptor.vinde(codProdus, cantitate); if (furnizor.equals("furnizor1")) mAdaptor.setDesfacere(null); if (furnizor.equals("furnizor2")) mAdaptor.setSale(null); if (furnizor.equals("furnizor3")) mAdaptor.setVente(null); }
18
public Adaptor getAdaptor () { return mAdaptor; } public void setAdaptor (Adaptor val) { this.mAdaptor = val; } } public class Adaptor implements IDesfacere, ISale, IVente { private Vente mVente; private Desfacere mDesfacere; private Sale mSale; public void vinde (String codProdus, Double cantitate) { if (mDesfacere != null) mDesfacere.vinde(codProdus, cantitate); if (mSale != null) {mSale.setCurrentProduct(codProdus); mSale.sell(cantitate.intValue()); } if (mVente != null) {mVente.setProduit(codProdus); mVente.setQuantite(cantitate); mVente.vendre(); } } public void sell (int quantity) { // aici putem sa scriem un cod asemanator cu cel din metoda vinde, dar care ar fi folosit atunci cnd adaptorul este folosit prin interfata ISale } public void vendre () { // aici putem sa scriem un cod asemanator cu cel din metoda vinde, dar care ar fi folosit atunci cnd adaptorul este folosit prin interfata IVente } // si in rest set-uri si get-uri ale clasei Adapter } public class Desfacere implements IDesfacere { public void vinde (String codProdus, Double cantitate) { System.out.println("Se foloseste clasa Desfacere pentru a cumpara produsul "+ codProdus); } } public class Sale implements ISale { private String currentProduct; public void sell (int quantity) { System.out.println("Se foloseste produsul " + this.currentProduct); } public String getCurrentProduct () { return currentProduct; } clasa Sale pentru a cumpara
19
public void setCurrentProduct (String val) { this.currentProduct = val; } } public class Vente implements IVente { private String produit; private Double quantite; public void vendre () { System.out.println("Se produsul "+ produit); } foloseste clasa Vente pentru a cumpara
public String getProduit () { return produit; } public void setProduit (String val) { this.produit = val; } public Double getQuantite () { return quantite; } public void setQuantite (Double val) { this.quantite = val; } }
Alte exemple de utilizare a adaptoarelor: - wrapper-ele din bazele de date federative (DB2) care fac legtur ntre baze de date eterogene; - coletele de transmis prin intermediul oficiilor potale diferite.
8.2.2 Compozit
ablonul compozit permite unui grup de obiecte s fie tratat ca un singur obiect. Cu ajutorul acestui ablon o operaie specific elementelor componente poate fi lansat i pentru un agregat, situaie n care operaia se lanseaz pentru toate elementele agregate. Una din situaiile n care ablonul compozit se face util este cea n care se gestioneaz structuri ierarhice. Diferena structural dintre noduri i frunze face ca lucrul cu structuri ierarhice s fie complex. Soluia propus de ablon este de a obine elementul
20
compozit (nodul n cazul structurilor ierarhice) din specializarea clasei destinat componentelor (frunzelor).
Fig. 8.17 ablonul compozit ablonul nu este util doar n cazul structurilor ierarhice, ci n toate relaiile de compunere n care operaiile unui grup de obiecte include operaiile unui singur obiect. Clasa destinat componentelor conine pe lng implementarea operaiilor specifice domeniului afacerii i declararea interfeei pentru accesarea i gestiunea componentelor copil (adugare, tergere, citire etc.). Clasa compozit implementeaz metodele de manipulare a componentelor copil, iar n cazul operaiilor specifice domeniului afacerii i deleg sarcinile operaiilor corespunztoare ale obiectelor copil. n exemplul urmtor este vorba de ziariti care fac parte din redacii, redacii care aparin de anumite publicaii (ziare, reviste etc.), publicaii ce aparin unor trusturi de pres. ablonul compozit ne ajut n acest caz ca o operaie specific unui ziarist (cea de a ataca) s fie lansat (de un mogul) pentru un ntreg trust, cu un efort minim, fr a apela fiecare ziarist n parte, ci apelnd doar operaia ataca a obiectului trust.
21
Dac declaraiile de mai sus ar face parte din pachetul sabloane_structurale, intrnd n pielea unui mogul am putea utiliza codul urmtor:
sabloane_structurale.Grup trust = new sabloane_structurale.Grup(); sabloane_structurale.Grup ziar = new sabloane_structurale.Grup(); sabloane_structurale.Grup redactie = new sabloane_structurale.Grup(); sabloane_structurale.Ziarist popescu = new sabloane_structurale.Ziarist(); sabloane_structurale.Ziarist ionescu = new sabloane_structurale.Ziarist(); sabloane_structurale.Ziarist altescu = new sabloane_structurale.Ziarist(); redactie.add(popescu); redactie.add(ionescu);
22
23
n exemplul din figura 8.19 cel care creaz obiectele de tip tranzacie (dintr-o interfa utilizator sau dintr-o procedur de import) nu trebuie s cunoasc detaliile faadei (claselor de contabilitate i gestiune), ci doar c o tranzacie, dup ce a fost creat, trebuie transmis faadei de tip ContabilitateSiGestiune pentru nregistrarea acesteia n contabilitate i afectarea gestiunilor corespondente:
//se creaza tranzactia cu datele din GUI/import Tranzactie tranzactie = new Tranzactie(); tranzactie.setSuma(11.); tranzactie.setTip("BUY"); //se creaza si se foloseste fatada fr s se cunoasc detaliile ei ContabilitateSiGestiune fatada = new ContabilitateSiGestiune(); fatada.setTranzactie(tranzactie); fatada.contabilizeaza();
n acest caz metoda contabilizeaza din faada ContabilitateSiGestiune include toat secvena de apelri a claselor: RegistruJurnal, CarteaMare, RegistruDeCasa, RegistruDeBanca, Stocuri.
8.2.4 Proxy
La modul general un proxy este o resurs (clas, server etc.) care funcioneaz ca o interfa pentru altceva. Un proxy nu face dect s delege aciunile mai departe unei alte clase.
Fig. 8.20 ablonul proxy Exist patru situaii n care se recomand s folosim un astfel de ablon: 1) Virtual proxy = o clas de acces ctre obiectele unei alte clase ce presupune utilizarea multor resurse; obiectul real este creat la prima cerere de acces la acesta. 2) Remote proxy = asigur o reprezentare local pentru un obiect dintr-un spaiu diferit de memorie; n RPC i CORBA o astfel de funcionalitatea o asigur un stub. 3) Protective proxy = clas de control al accesului la obiecte mai sensibile din punct de vedere al securitii. Obiectul proxy verific dac expeditorul are dreptul s transmit mesaje destinatarului.
24
4) Smart proxy = clas care nu doar transmite mai departe o operaie de realizat, ci realizeaz n plus o serie de aciuni, cum ar fi: a. numr referirile la un anumit obiect; b. ncarc obiecte persistente la prima utilizare; c. verific dac obiectul adevrat este blocat pentru alte accese (asigur prelucrri tranzacionale i diferite niveluri de izolare) Exemplificm un protective proxy:
//atat obiectul proxy cat si realSubject implementeaza aceeasi interfata public interface Subject { public void request (); } public class RealSubject implements Subject { public void request () { System.out.println(this.getClass().getSimpleName() operatia dorita!"); } } public class Proxy implements Subject { private RealSubject mRealSubject = new RealSubject(); public void request () { //daca numele utilizatorului sistemului de operare = .... if (System.getProperty("user.name").contentEquals("Florin")) { mRealSubject.request(); } else { System.out.println(this.getClass().getSimpleName() + executia operatiei dorite!"); } } }
"
executa
"
refuza
8.2.5 Decorator
ablonul decorator ofer o modalitate de a aduga comportament unor obiecte individuale fr a fi necesar crearea unei noi clase pentru a realiza acest lucru. Adugarea de noi funcionaliti obiectelor poate fi fcut conform acestui ablon n mod dinamic, n timpul execuiei programului. Soluia derivrii este i ea acceptabil n general, dar este important de tiut c nu este unica soluie de adugare a unor noi comportamente i c se pot evita ierarhii stufoase. Acest ablon adaug funcionaliti unor instane specifice ale unei clase i nu ntregii clase, permind adaptarea clasei respective, fr a crea clase derivate care s duc la ierarhii complexe. Soluia ablonului const n nglobarea uneia sau mai multor componente ntr-un obiect (cu rolul decorator), fiecare component coninnd metodele noii funcionaliti.
25
Fig. 8.21 ablonul Decorator Interfaa Component definete interfaa obiectelor ce adaug noi funcionaliti. Interfa Decorator este destinat obiectelor ce vor beneficia la run-time de noi functionaliti. Clasa ConcreteDecorator este o implementare a interfeei Decorator. Metoda addedBehavior este doar un exemplu de specializare i nu face parte din specificul ablonului, ci din contra: specificul ablonului este de aduga funcionaliti n componente concrete i nu direct prin astfel de noi metode. n exemplul urmtor vom utiliza clase abstracte n loc de interfee pentru a putea include ntr-un Decorator atribute nonstatice. El face referire la un grup de clase de control menit s gestioneze drepturile accesibile inclusiv prin interfee grafice.
Fig. 8.22 Exemplu de ablon decorator Clasa abstract GrupDrepturi are rolul de Decorator, deoarece beneficiaz de noi funcionaliti cu ajutorul coleciei de drepturi. Un Decorator concret este GrupDrepturiPentruContabili. Clasa abstract Drept are rolul de component. Clasa DreptModulTranzactie este o component concret, ce include metode pentru utilizarea tuturor drepturilor specifice unui Modul de Tranzactie.
26
Fig. 8.23 ablonul mediator Mediator - definete interfaa de comunicare ntre obiecte ConcreteMediator - implementeaz interfaa Mediator i coordoneaz comunicarea ntre objecte. tie care sunt toate obiectele ce doresc s comunice i scopurile acestor comunicri. ConcreteColleague comunic cu alte obiecte colegi cu ajutorul mediatorului. ablonul mediator se poate combina cu ablonul singleton, dac o clas concret mediator are sens s fie instaniat o singur data. ntr-un astfel de caz, dac aplicaia abstractizeaz mai multe grupuri de colegi, pentru fiecare grup se poate crea cte o clas concret mediator care s fie instaniat o singur dat. ablonul mediator nu trebuie confundat cu ablonul proxy. Diferena principal este faptul c mediatorii i colegii nu sunt creai pe baza aceleiai interfee (sau clase abstracte). Un proxy este imaginea unui alt obiect, prin urmare structura unui proxy depinde destul de mult de structura obiectului destinaie. Nu degeaba ablonul proxy este inclus n grupul abloanelor structurale. Structura unui mediator nu este imaginea n oglind a unui obiect coleg, ceea ce i d posibilitatea s transmit mesajele spre mai multe tipuri de clase concrete de colegi, altfel spus s medieze mesajele ntre colegi cu structuri diferite. Exist asemnri ntre ablonul mediator i alte abloane la lista de avantaje: un coleg (binevoitor) poate s transmit anonime (avantaj ntlnit i la proxy);
27
un coleg (rspndac) poate s transmit acelai mesaj mai multor obiecte (avantaj ntlnit i la ablonul compozit); un mediator (securist) poate s foloseasc liste de control al accesului (avantaj ntlnit i la proxy); un mediator (mai lene) poate atepta mai multe mesaje de la colegi pentru a lansa o anumit operaiune (avantaj specific ablonului mediator). Exemplu de folosire a ablonului moderator: organizarea unor vizite (de exemplu la Moscova) poate fi realizat prin intermediul unui moderator (de exemplu ambasadorul rus la Bucureti). Utiliznd terminologia acestui ablon, colegi sunt persoanele ce urmeaz s se ntlneasc. Din pcate, ablonul mediator nu reflect i relaii de subordonare ntre colegi. Un alt exemplu (total rupt de cel anterior) este cel al unei reele de spionaj n care colegi sunt spionii i persoanele decidente din structurile de informaii. Mediatorii n acest exemplu sunt persoanele de legtur, care fac ca de cele mai multe ori colegii s nu se cunoasc ntre ei.
8.3.2 Observator
ablonul observator este un ablon de proiectare n care un obiect gestioneaz o list cu proprii dependeni, pe care i anun automat de eventualele modificri de stare, de obicei prin apelarea anumitor metode. De multe ori acest ablon este folosit pentru implementarea sistemelor distribuite.
Fig. 8.24 ablonul observator Clasa Subject este o clas abstract (sau o interfa) ce asigur ataarea i scoaterea observatorilor. Clasa conine pe lng o list privat cu observatori i urmtoarele metode:
28
attach( ) adaug un nou observator n list; detach( ) elimin un observator din list; notifyObserver ( ) anunt fiecare observator asupra unei schimbri de stare prin apelarea metodelor update( ) ale acestora. Clasa ConcreteSubject este elementul de interes al observatorilor. Ea trimite o notificare tuturor observatorilor prin apelarea metodei notifyObserver( ) din clasa ei printe. Clasa ConcreteSubject conine pe lng interfaa Subject metoda GetState ce returneaz starea subiectului de observat. Clasa Observer definete o interfa pentru anunarea tuturor observatorilor asupra modificrilor survenite n subiect. Interfaa const ntr-o metod ce va fi suprascris de fiecare observator concret. Clasa ConcreteObserver gestioneaz o referin ctre clasa ConcreteSubject i conine operaia update( ). Cnd acest operaie este apelat de ctre subiect, ConcreteObserver apeleaz operaia GetState a subiectului pentru a-i actualiza informaia privind starea subiectului. Operaia update( ) poate primi eventual parametri cu informaii generale ale evenimentului aprut, informaii utile observatorului.
public class Observer { public void update () { } } public abstract class Subject { private ArrayList<Observer> mObserver = new ArrayList<Observer>(); public void attach (Observer o) { mObserver.add(o); } public void detach (Observer o) { mObserver.remove(o); } public void notifyObserver () { for (Observer o : mObserver){ o.update(); } } } public class ConcreteSubject extends Subject { private String state; public String getState () { return state; } public void setState (String val) { this.state = val; System.out.println("Subiectul " + this.toString() + isi anunte toti observatorii de schimbarea starii!"); this.notifyObserver(); } }
" incepe sa
29
public class ConcreteObserver extends Observer { private ConcreteSubject mConcreteSubject = new ConcreteSubject(); public void update () { System.out.println("Observatorul " + this.toString() + " a fost anuntat de schimbarea starii Subiectului " + mConcreteSubject.toString() + "!"); System.out.println("Observatorul " + this.toString() + " citeste noua stare " + mConcreteSubject.getState() + "!"); } public ConcreteSubject getmConcreateSubject () { return mConcreteSubject; } public void setmConcreteSubject (ConcreteSubject s) { mConcreteSubject = s; } }
ablonul observator este utilizat atunci cnd modificarea strii unui obiect afecteaz alte obiecte i nu se tie la momentul scrierii codului exact ce obiecte vor trebui anunate. Exemplu de folosire a ablonului observer: implementrarea modurilor de lucru, cnd ntr-o fereastr de dialog utilizarea unui obiect poate presupune disponibilizarea sau indisponibilizarea altor obiecte. n terminologia acestui ablon toate obiectele de pe un formular sunt observatori, iar subiectul concret de observat este chiar formularul. Fiecare obiect n parte nu trebuie s modifice direct celelalte obiecte de pe formular pentru c ar trebui s cunoasc mulimea acestora. Aceeai funcionalitate a modurilor de lucru ar putea fi implementat i cu ajutorul ablonului mediator.
30
Fig. 8.25 ablonul lan de responsabiliti Conform acestui ablon un client cere unui obiect ConcreteHandler s fie realizat o aciune, dar acesta poate s transmit sarcina unui alt obiect ConcreteHandler n funcie de un anumit algoritm. Avantajele unui astfel de ablon: - expeditorul poate s nu cunoasc exact care este destinatarul final al cererii sale, pe el interesndu-l doar ca respectiva sarcin s fie ndeplinit (asemnare cu abloanele proxy i mediator); - clasele intermediare pot alege destinatarii, gestionnd eventual i gradul de solicitare al acestora i gradul de solicitare a sistemelor de calcul pe care ruleaz acetia (asemnare cu ablonul proxy, pentru c i un proxy poate s aleag RealSubject n funcie de gradul de solicitare); - o cerere poate fi procesat de mai muli destinatari, n acelai timp, secvenial sau respectnd chiar anumite fluxuri de cereri (asemnare cu ablonul mediator); - clasele intermediare pot realiza log-uri ale cererilor (asemnare cu abloanele proxy, mediator, observator etc.); - lipsa oricrui potenial destinatar poate fi aflat de ctre expeditor printr-un mesaj primit de la clasele intermediare (asemnare cu abloanele proxy i mediator). Din cele de mai sus reies o serie de asemnri ntre ablonul mediator i lanul de responsabiliti, mai exact rolul Mediator seamn cu ConcreateHandler i rolul Coleg seamn cu rolul Client. Deosebirile dintre ablonul mediator i lanul de responsabiliti constau n: - un mediator este de obicei singur pentru un grup de colegi, dar obiectele de tip ConcreateHandler au sens s fie mai multe (ca s ias lanul); - mediatorul nu este un obiect care s i execute efectiv aciunea cerut, dar n lanul de responsabiliti, un obiect de tip ConcreateHandler va realiza aciunea cerut; - n ablonul mediator este vorba de mai muli colegi, dar la un lan de responsabiliti exist un singur obiect cu rolul de client. Deosebirea principal dintre ablonul proxy i lanul de responsabiliti este faptul c un ConcreateHandler din lanul de responsabiliti este n acelai timp un proxy (poate da mai departe sarcina), dar i un RealSubject (poate executa sarcina). Un exemplu de lan de responsabiliti este modul de autorizare a unor tranzacii de ctre anumii angajai n funcie de mrimea acestor tranzacii. Aprobarea unui credit poate fi fcut la nivelul unui ofier de credit, manager al departamentului de credite, ef de filial sau sucursal i n rare cazuri un credit are nevoie i de aprobarea managerilor din central. Clasa Functie are rolul de ConcreteHandler, n ea existnd:
31
- un atribut cu obiectul ce reprezint nivelul ierarhic superior (un obiect creat tot pe baza clasei Functie); - un atribut ce ne indic limita creditului ce poate fi aprobat fr a fi trimis i la nivelul ierarhic superior.
Fig. 8.26 Exemplu de utilizare a ablonului lan de responsabiliti Metoda proceseazDosarCredit poate fi implementat astfel:
public void proceseazaDosarCredit( Credit c ) { if ( c.suma <= limitaCredit ) { //proceseaz efectiv dosarul n sensul aprobrii sau respingerii lui } else if ( superior != null ) superior.proceseazaDosarCredit(c); }
Exemplul de mai sus poate fi extins prin crearea a cte o clas specializat din clasa Functie pentru fiecare funcie (ofier de credit, manager al departamentului de credite, ef de filial sau sucursal, manager din central) i suprascrierea metodei proceseazDosarCredit n aa fel nct algoritmul de decizie privind cui i cade n sarcin dosarul respectiv s difere la fiecare nivel. Exist variante ale acestui ablon n care obiectele de tip ConcreateHandler: - retransmit cererea mai multor obiecte, formndu-se arbori de responsabiliti; - retransmit recursiv cererea; - rezolv o parte a problemei i transmit mai departe doar partea neprocesat din comand. ablonul seamn cu aruncarea mei moarte n ograda vecinului, n funcie de starea de putrefacie a bietului animal. Cu ct starea de putrefacie este mai mare cu att pisica va ajunge la un vecin mai ndeprtat (n sperana c toi vecinii vor pstra aceeai direcie i fiecare vecin are propria limit de suportare a mirosului). Biata pisic se oprete la primul vecin care i suport mirosul (dac nu rmne n perpetuum mobile).
32
33
m.setStare1(stare1); m.setStare2(stare2); c.addMemento(m); } public void restoreFromMemento (int index) { Memento m = c.getMemento(index); this.stare1 = m.getStare1(); this.stare2 = m.getStare2(); } /*plus metodele get si set specifice acestor atribute*/ }
Clientul care ar folosi un astfel de ablon ar ncepe prin a modifica strile obiectului Originator i ar utiliza din cnd n cnd metoda saveToMemento pentru a salva diferitele stri ale acestuia. Revenirea la o stare anterioar se face tiind un numr de ordine al acesteia n cadrul coleciei de stri (numrtoarea ncepe de la 0, cel puin n exemplul urmtor).
Originator orig = new Originator(); orig.setStare1("ALFA"); orig.setStare2(6); orig.saveToMemento(); orig.setStare1("BETA"); orig.setStare2(7); orig.saveToMemento(); orig.setStare1("GAMA"); orig.setStare2(8); orig.saveToMemento(); orig.restoreFromMemento(1);/*aici se revine la starile BETA & 7*/ System.out.println(orig.getStare1()); System.out.println(Integer.toString(orig.getStare2()));
8.3.5 Strategie
ablonul strategie ne ajut s alegem un anumit algoritm de utilizat n funcie de un context, n momentul rulrii. ablonul conine un grup de algoritmi, fiecare din acetia fiind ncapsulat ntr-un obiect (the tip ConcreteStrategy). Clienii ce folosesc algoritmii (instane ale clasei cu rolul Context) nu depind de acetia, variind n mod independent.
34
Fig. 8.28 ablonul strategie Obiectele de tip context conin cte un obiect strategy, dar alegerea unei strategii concrete se realizeaz n funcie de context. Conform ablonului strategy comportamentul unei clase nu ar trebui s fie motenit, ci specific contextului n care ruleaz i ncapsulat utiliznd interfee. De exemplu, o aplicaie de salarizare poate lua n calcul diferitele tipuri de salarizare: n regie sau n acord. n practic, pentru fiecare dintre aceste tipuri de salarizare exist diverse variante, putndu-ne atepta ca la un moment dat s se doreasc adugarea unui nou algoritm la lista iniial. Conform ablonului strategie realizm urmtoarele clase:
Fig. 8.29 Exemplu de ablon strategie Dac inem mori s nu utilizm ablonul strategie la realizarea unei aplicaii care s implementeze diferitele tipuri de salarizare ntr-o posibil metod CalculSalarBrut, atunci: 1) n metoda CalculSalarBrut am scrie o structur alternativ, n ramurile creia am include algoritmii specifici fiecrui tip de salarizare (metoda capt volum i mprirea sarcinilor la mai muli programatori devine ciudat cnd este vorba de o singur metod); sau 2) am crea o ierarhie de angajai n funcie de tipul de salarizare (clasa angajat poate face parte din ierarhii fcute dup alte criterii i folosirea unei ierarhii cu mai multe criterii de specializare ridic probleme de redundan a codului). Cele dou soluii alternative nu separ clientul (i eventual ierarhia din care face parte clientul) de comportamentul dorit. Un alt exemplu, pentru iubitorii de jocuri electronice (mai ales de shootere): dac avem o ierarhie de lupttori cu diverse caracteristici i o metod atac, ce face efectiv metoda atac nu ine de ierarhia de lupttori, ci de arma deinut la momentul respectiv. n acest exemplu, lupttorii au rolul Context, iar diferitele arme au rolul de ConcreteStrategy.
35
Fig. 8.30 ablonul iterator Operaii ale elementelor coleciei de parcurs ar putea fi: - currentItem (ghici ce face fiecare operaie); - first; - last; - next; - previous; - hasNext; - hasPrevious; - isFirst; - isLast. ablonul iterator are un grad mai mare de importan n mediile de dezvoltare n care nu sunt implementate complet tipuri de date compuse (vectori, liste, stive). l ntlnii implementat n RecordSet-urile ADO, DAO sau RDO specifice mediilor de dezvoltare Microsoft.
36
Fig. 8.31 ablonul interpreter O expresie terminal este o expresie care nu poate fi mprit n expresii mai mici, ca n cazul unei expresii nonterminale. O expresie nonterminal se comport ca un evaluator de expresii. Acest evaluator trebuie s mpart expresia de interpretat n mai multe pri cu un parser. Tot evaluatorul trebuie s parcurg fiecare parte a expresiei: - cnd ntlnete un operand (o variabil pe care are voie utilizatorul s o foloseasc ntr-o anumit formul) l introduce ntr-o stiv; - cnd ntlnete un operator extrage din stiv valorile operanzilor afectai i introduce n loc rezultatul aplicrii operatorului asupra operanzilor. Dac se pleac de la o expresie corect la sfritul parcurgerii acesteia n stiva utilizat va rmne o singur valoare, ea fiind chiar rezultatul interpretrii. Exemple de aplicaii n care ar putea fi utilizat ablonul interpreter: - aplicaii de salarizare; - aplicaii de procesare a datelor la importul sau exportul lor din diferite sisteme; - aplicaii de interpretare a unui dialect SQL sau de traducere dintr-un dialect SQL n altul. Un interpretor poate fi vzut i ca o cutie neagr creia i se d o expresie i care returneaz un rezultat. Cum nu trebuie s reinventm roata putem folosi interpretore deja existente, cum ar fi cele pentru scripturi:
import javax.script.ScriptEngineManager; import javax.script.ScriptEngine; import javax.script.ScriptException; ScriptEngineManager mgr = new ScriptEngineManager(); ScriptEngine jsEngine = mgr.getEngineByName("JavaScript"); try {
37
Rularea codului de mai sus ar duce la evaluarea expresiei trimis metodei eval i anume la calculul i afiarea variabilei c.
Fig. 8.32 ablonul command Clientul instaniaz obiectul de tip comand i l pregtete pentru a fi apelat la un moment ulterior (transmindu-l invoker-ului). Obiectul invoker conine o list de comenzi i decide cnd va fi apelat obiectul comand. Obiectul receiver este cel care va efectua o aciune ca urmare a lansrii n execuie a comenzii. Beneficiile acestui ablon in de faptul c execuia unei anumite metode poate fi pregtit din timp, n aa fel nct aceasta s fie lansat fr s i se tie numele, obiectul de care aparine i momentul exact al execuiei. ablonul command poate fi utilizat pentru a realiza aplicaii cu: - funcionaliti de tip undo; - comportament tranzacional; - progress bar sincronizat cu execuia unui grup de comenzi; - funcionaliti de tip wizard; - nregistrri de macro-uri; De exemplu, dac un ppuar (client) dorete s pun o ppu (receiver) s se bucure (concretecommand) folosete un ef de campanie (invoker), iar acesta i transmite ppuii s opie (metoda action din ablon).
38