TemplateMethod

Producatorul abstract (Abstract Factory)





Definitie
Ofera o interfata pentru crearea unor familii de obiecte inrudite sau dependente intre ele, fara a specifica clasa lor concreta. Se mai numeste si kit.

Context
Consideram un generator de interfete grafice utilizator (GUI) care ofera suport pentru standarde multiple privind modul de prezentare a elementelor interfetei. Exemple de asemenea standarde sunt: Motif, Presentation Manager (PM) etc. Diferitele moduri de prezentare ("look & feel") definesc diferite stiluri si modalitati de comportare ale elementelor de control (bare de defilare, ferestre, butoane, meniuri etc). Pentru a asigura portabilitatea intre diferitele standarde, o aplicatie nu trebuie sa fixeze prin cod un anumit tip de elemente de control.
Problema se poate rezolva prin definirea unei clase abstracte, pe care o vom numi WidgetFactory si care declara o interfata pentru crearea fiecarui element de control GUI. Pentru fiecare element de control va exista cate o clasa abstracta care il modeleaza (de exemplu: Window, ScrollBar) precum si mai multe clase concrete, corespunzatoare diverselor standarde de prezentare. Interfata WidgetFactory are cate o operatie care returneaza un nou obiect din fiecare clasa abstracta ce modeleaza un element de control. Clientii vor apela aceste operatii pentru a obtine instante ale elementelor de control, fara sa le "pese" de standardul de prezentare, deci fara a sti la ce clase concrete apartin acele instante:

Pentru fiecare standard de prezentare exista cate o subclasa concreta a lui WidgetFactory care implementeaza operatiile de creare a elementelor de control conform standardului respectiv. In felul acesta se forteaza o anumita dependenta intre clasele concrete  care tin de un anumit standard de prezentare, evitandu-se situatii in care unei ferestre Motif de exemplu, i se ataseaza o bara de defilare PM.
 

Motivatii

Sablonul Abstract Factory se utilizeaza cand:

un sistem trebuie sa fie independent de modul in care produsele sale sunt create, compuse si reprezentate;
un sistem trebuie sa fie configurat la un moment dat cu una din mai multe familii de produse;
trebuie fortata restrictia ca produsele dintr-o anumita familie sa fie utilizate impreuna si nu amestecate cu produsele altei familii;
se doreste crearea unei biblioteci de produse pentru care sunt relevante doar interfetele, nu si implementarile (de exemplu, in cazul generatorului de interfete analizat mai inainte, nu ne intereseaza cum sunt desenate pe ecran diversele elemente de control, ci doar care sunt efectele actionarii lor de catre utilizator si aceste efecte sunt asemenatoare, indiferent de standardul de prezentare).


Solutie

In figura de mai jos este data structura de clase care constituie sablonul Abstract Factory:

In mod normal, la executie se creaza cate o singura instanta a claselor ConcreteFactory. Aceste instante vor crea produse avand o implementare particulara. Pentru a obtine produse diferite, clientii trebuie sa utilizeze alte clase ConcreteFactory. Clasa AbstractFactory paseaza sarcina crearii produselor spre subclasele ei ConcreteFactory.
 

Consecinte

Izolarea claselor concrete: deoarece o clasa producator (factory) incapsuleaza responsabilitatea si procesul crearii obiectelor-produs, ea izoleaza clientii fata de clasele de implementare. Clientii manipuleaza instantele doar prin intermediul interfetelor lor abstracte. Numele claselor care modeleaza produsele apar doar in implementarile producatorilor concreti (ConcreteFactory)., nu si in codul clientului. De exemplu, clientul lucreaza doar cu instante ale clasei Window, nu cu PMWindow sau MotifWindow.
Inlocuirea familiilor de produse: deoarece un producator concret apare intr-un singur loc intr-o aplicatie, si anume acolo unde este instantiat, este usor ca el sa fie schimbat la un moment dat. Intrucat un producator abstract creaza o familie completa de produse, schimbarea se aplica la intreaga familie.
Pastrarea consistentei produselor: sablonul AbstractFactory, prin structura sa, forteaza o aplicatie sa lucreaza cu obiectele dintr-o singura familie la un moment dat.
Dezavantaj: extinderea unui producator abstract, in vederea crearii de produse noi in cadrul aceleiasi familii nu este simpla, deoarece implica modificarea tuturor subclaselor lui AbstractFactory.
 
Implementare
O aplicatie necesita de regula cate o singura instanta a clasei ConcreteFactory per familie de produse. De aceea, este bine ca aceste clase sa fie implementate ca Singleton.

Clasa AbstractFactory declara doar o interfata pentru a crea produsele. Cade in sarcina claselor ConcreteProduct crearea propriu-zisa. Aici cel mai bine se poate aplica sablonul FactoryMethod pentru fiecare produs din familie.

Extinderea producatorilor: asa cum s-a aratat mai sus, operatia de modificare a unui producator pentru a-l determina sa creeze noi produse nu este simpla. Ceea ce se poate face in acest sens este ca, in loc de a avea cate o operatie de forma CreateProduct pentru fiecare produs in parte, sa se definesca o singura operatie Create care sa primeasca un parametru de identificare a tipului de produs dorit (vezi exemplul de la Factory Method). Aceasta varianta este flexibila, dar mai putin sigura. Problema care se ridica aici este aceea ca toate instantele returnate de operatia Create vor avea aceeasi interfata, si anume cea corespunzatoare tipului returnat de Create, iar clientul nu va putea determina intotdeauna care este clasa reala de care apartine instanta obtinuta.
 


Tema

Sa se scrie un program care aplica sablonul Abstract Factory pentru urmatoarea structura de clase:

Un fisier de text in varianta ASCII va contine un text oarecare, iar cel in varianta HTML va contine acelasi text, dar incadrat intr-o structura ca cea descrisa la tema din lucrarea Ierarhii de obiecte (Composite). Se considera ca textul este format dintr-un singur paragraf.
La crearea unui tabel se vor preciza numarul de linii si de coloane, fara sa ne intereseze in mod deosebit continutul celulelor. In varianta ASCII tabelul va avea grila realizata cu ajutorul caracterelor '-' (pentru liniile orizontale) , '|' (pentru liniile verticale) si '+' pentru intersectiile liniilor verticale cu cele orizontale. In varianta HTML tabelul va fi pur si simplu creat cu tag-urile <table>, <tr> si <td> intr-un fisier HTML.
Pentru liste, in varianta ASCII elementele listei vor fi indentate spre dreapta cu cate un tab si vor fi marcate cu cate un caracter '*'. De exemplu:
Lista ASCII:
    *element_lista_1
    *element_lista_2
    *etc
Listele in varianta HTML vor fi create in fisiere HTML, cu ajutorul tag-urilor <ul> si <li>.

TemplateMethod