Iterator AbstractFactory

Metode sablon (Template Method)








Definitie
Sablonul defineste scheletul algoritmului dintr-o operatie, lasand ca anumiti pasi sa fie (re)definiti in subclase.

Context
Consideram un generator de aplicatii gen desktop, abordat si la descrierea sablonului Constructorul Virtual (Factory Method). Asa cum s-a aratat acolo, asemenea aplicatii sunt de regula concepute sa lucreze cu documente. Un generator de aplicatii desktop include abstractiuni care sa asigure suportul pentru operatii ca: deschiderea, crearea sau salvarea unui document. Abstractiunile de baza sunt clasele Application si Document. Ambele sunt clase abstracte, iar clientii trebuie sa creeze subclase din ele, pentru a-si defini propriile aplicatii. Pentru a genera o aplicatie de desenare, de exemplu, trebuie sa se defineasca subclasele DrawingApplication, respectiv DrawingDocument; pentru o aplicatie din categoria foi electronice de calcul (spreadsheet) probabil este necesar sa se defineasca subclasele SpreadsheetApplication si SpreadsheetDocument.

Clasa abstracta Application defineste algoritmul pentru deschiderea si citirea unui document in cadrul operatiei OpenDocument:
 
void Application::OpenDocument(const char* name ) {
    if (!CanOpenDocument(name) {
        //documentul nu poate fi prelucrat
        return;
    }
    Document *doc = CreateDocument( ); //vezi FactoryMethod
    if (doc) {
        docs -> AddDocument(doc);
        doc -> Open( );
        doc -> DoRead( );
    }
}

Dupa cum se poate observa, OpenDocument defineste toti pasii necesari pentru deschiderea unui document, si anume: verifica daca documentul poate fi deschis (CanOpenDocument), creaza obiectul Document specific aplicatiei (CreateDocument), il adauga la lista documentelor (AddDocument) si citeste documentul dintr-un fisier (Open + DoRead).
Operatia OpenDocument reprezinta o metoda sablon. O asemenea metoda defineste un algoritm in termenii unor operatii abstracte, pe care subclasele le redefinesc pentru a obtine un anumit comportament concret. Astfel, in cazul exemplului nostru, subclasele lui Application redefinesc operatiile CanOpenDocument si CreateDocument, iar subclasele lui Document redefinesc Open si DoRead.
In cadrul unei metode sablon este "batuta in cuie" ordinea in care se executa pasii algoritmului respectiv, dar subclasele care redefinesc acei pasi au libertatea sa-i conceapa in functie de necesitati.

Motivatii

Sablonul Template Method se utilizeaza in urmatoarele situatii:

pentru a implementa partile invariante ale unui algoritm o singura data, lasand subclaselor sarcina de a implementa partile care variaza;
cand partile comune ale unor subclase trebuie "scoase in factor comun" si localizate intr-o singura clasa, pentru a evita duplicarea de cod; acesta este un exemplu de "reproiectare in vederea generalizarii": mai intai se identifica diferentele din codul existent si apoi se constituie operatii noi cu diferentele respective, iar in final, codul se inlocuieste cu o metoda sablon care apeleaza una dintre operatiile noi;
pentru a controla extinderea subclaselor, si anume: se poate defini o metoda sablon care apeleaza asa-numite operatii-hook ("carlige") in puncte specifice, permitand astfel ca extensiile sa se realizeze doar in acele puncte.


Solutie

In figura de mai jos este data structura de clase care constituie sablonul Template Method:

void AbstractClass::TemplateMethod( ) {
    //. . .
    PrimitiveOperation1( );
    //. . .
    PrimitiveOperation2( );
    //. . .
}


Consecinte

Metodele sablon reprezinta o tehnica fundamentala de reutilizare a codului. Ele au o importanta deosebita in cadrul bibliotecilor de clase, deoarece constituie un mijloc de factorizare a aspectelor comune ale comportamentului claselor respective.

Metodele sablon reprezinta o structura de control inversat, numita adesea "principiul Hollywood", adica: "Nu ne chema tu, ca te chemam noi". Aceasta inseamna ca o clasa parinte apeleaza operatiile unei subclase si nu invers.

Metodele sablon poate apela urmatoarele categorii de operatii:

Este important pentru metodele sablon sa se specifice clar care operatii sunt hook-uri (care POT fi redefinite) si care sunt abstracte (care TREBUIE redefinite). In vederea reutilizarii eficiente a unei clase abstracte, proiectantii subclaselor ei trebuie sa inteleaga care sunt operatiile concepute anume spre a fi redefinite.
O subclasa poate EXTINDE o operatie din clasa parinte prin redefinirea acelei operatii si apeland explicit operatia omonima din parinte:
 
void ClasaDerivata::Operatie( ) {
    //secventa specifica pt. ClasaDerivata
    ClasaParinte::Operatie( );
}

Din pacate este foarte usor sa se omita apelul la operatia din clasa parinte. Pentru a evita acest lucru, se poate utiliza o metoda sablon care permite clasei parinte sa preia controlul asupra modului in care subclasele realizeaza extinderea. Ideea este ca in metoda sablon din clasa parinte sa se apeleze un hook pe care subclasele il pot redefini:
 

void ClasaParinte::Operatie( ) {
    //secventa specifica pt. ClasaParinte
    Hook( );
}
void ClasaParinte::Hook( ) { } //in clasa parinte hook-ul nu face nimic

void ClasaDerivata::Hook( ) {
    //secventa specifica pt. ClasaDerivata
}

Implementare
 

In C++ operatiile primitive pe care o metoda sablon le apeleaza pot fi declarate in sectiunea protected. In felul acesta ne asiguram ca ele nu pot fi apelate din exterior. Operatiile primitive care TREBUIE redefinite vor fi declarate ca pur-virtuale. Metoda sablon insasi nu trebuie redefinita, de aceea ea poate sa nu fie virtuala.

Un obiectiv important in proiectarea metodelor sablon il constituie minimizarea numarului de operatii primitive pe care subclasele trebuie sa le redefineasca.

Pentru a usura identificarea operatiilor care trebuie redefinite, se pot utiliza conventii de nume. De exemplu se pot prefixa numele operatiilor cu "Do": DoRead, DoCreate etc.

Tema
Se cere sa se scrie un program care sa realizeze: Datele pot fi: numere, siruri alfanumerice si constante booleene (sub forma True sau False). Fiecare asemenea data este insotita de o specificatie de formatare care indica modul in care trebuie afisata data in fisierul de iesire si care poate fi: Se presupune ca in fisierul de intrare pe un rand sunt trecute: formatul de afisare urmat de valoarea constantei, separarea intre cele doua fiind facuta prin spatii.
Programul va aplica sablonul Template Method astfel:
-clasele cu rol de ConcreteClass vor modela cele 3 tipuri de constante si vor contine operatiile: -clasa cu rol de AbstractClass va contine metoda sablon care citeste cate o linie din fisierul de intrare, identifica formatul de afisare si, in functie de acesta, creaza cate un obiect care sa modeleze tipul de constanta corespunzator (aici se va aplica eventual Factory Method), apoi apeleaza, in ordine, cele 3 operatii de mai sus.
Exemplu de fisier de intrare:
X(8) spaga
X(6) 75
9999,99 444
A True
N False
9999999 0.1111
X(10) a<b;c>d

In fisierul de iesire va rezulta:
spagabbb
75bbbb
b444,00
T
0
bbbbbb0
a<b;c>dbbb

(cu b s-a notat blancul).

Iterator AbstractFactory