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.
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( ); //. . . } |
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:
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( ) {
|
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.TemaUn 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.
-clasele cu rol de ConcreteClass vor modela cele 3 tipuri de constante si vor contine operatiile:Exemplu de fisier de intrare:-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.
- CitesteConstanta = citirea valorii constantei din fisierul de intrare;
- Formateaza = scrierea constantei intr-un buffer conform formatului de afisare;
- ScrieConstanta = scrierea buffer-ului in fisierul de iesire;
In fisierul de iesire va rezulta:
spagabbb
75bbbb
b444,00
T
0
bbbbbb0
a<b;c>dbbb
(cu b s-a notat blancul).