Inginerie Software
Curs 12
Şabloane de proiectare
(Design patterns)
1. Introducere
Toate sistemele orientate pe obiecte bine structurate abundă în şabloane (patterns),
mergând de la mecanisme care conturează forma sistemului în ansamblu, până la şabloane
locale (cum ar fi de exemplu modul de tratare a excepţiilor).
Definiţie: Un şablon reprezintă o soluţie comună a unei probleme într-un anumit context.
Importanţa şabloanelor (standardelor) în construirea sistemelor complexe a fost de
mult recunoscută în alte discipline. În cadrul comunităţii proiectanţilor de software orientat
obiect (OO), ideea de a aplica şabloane se pare că a fost inspirată de propunerea unui arhitect,
Christopher Alexander, care a lansat iniţiativa folosirii unui limbaj bazat pe şabloane pentru
proiectarea clădirilor şi a oraşelor. Acesta afirma că: "Fiecare şablon descrie o problemă care
apare mereu în domeniul nostru de activitate şi indică esenţa soluţiei acelei probleme, într-un
mod ce permite utilizarea soluţiei de nenumărate ori în contexte diferite".
Deşi în domeniul sistemelor OO soluţiile sunt exprimate în termeni de obiecte şi
interfeţe (în loc de ziduri, uşi, grinzi etc), esenţa noţiunii de şablon este aceeaşi, adică de
soluţie a unei probleme într-un context dat.
2. Şabloane de proiectare
Proiectarea sistemelor OO este o activitate dificilă, iar proiectarea sistemelor OO
reutilizabile aduce un plus de dificultate. Soluţia trebuie să fie specifică problemei, dar
totodată suficient de generală pentru a putea fi aplicată şi pe viitor, pentru a evita în ultimă
instanţă "reinventarea roţii" (sau cel putin pentru a minimiza acest lucru).
Un proiectant fără experienţă este de multe ori copleşit de multitudinea opţiunilor
disponibile şi are tendinţa de a se întoarce la tehnicile non-obiectuale pe care le-a folosit în
trecut. Un proiectant experimentat ştie că NU TREBUIE să rezolve fiecare problemă începând
de la zero, ci reutilizând soluţii din proiecte anterioare. Atunci când descoperă o soluţie bună
o va folosi mereu. Acest tip de experienţă este o parte din ceea ce conferă unui proiectant
statutul de expert.
Şabloanele de proiectare sunt de fapt o memorare pentru posteritate a experienţei în
domeniul proiectării sistemelor OO.
Elementele de bază ale unui şablon de proiectare:
numele şablonului: descrie sintetic problema rezolvată de şablon şi soluţia adoptată
1
Inginerie Software
problema: o descriere mai largă a problemei rezolvate şi a contextului în care ea apare
soluţia: o descriere a elementelor de proiectare utilizate şi a relaţiilor dintre ele.
Soluţia nu descrie un proiect particular sau o implementare concretă, ci un ansamblu
abstract de clase şi obiecte ce rezolvă un anumit gen de probleme de proiectare
consecinţele şi compromisurile implicate de folosirea şablonului: acestea pot privi
impactul asupra flexibilităţii, extensibilităţii sau portabilităţii sistemului, după cum pot
să se refere la aspecte ale implementării sau limbajului de programare utilizat.
Compromisurile sunt de cele mai multe ori legate de spaţiu şi timp.
3. Criterii de clasificare
a) scop: şabloanele pot fi, din acest punct de vedere: creaţionale, structurale
sau comportamentale.
o şabloanele creaţionale (creational patterns) privesc modul de creare a
obiectelor.
o şabloanele structurale (structural patterns) se referă la compoziţia claselor sau
a obiectelor.
o şabloanele comportamentale (behavioral patterns) caracterizează modul în
care obiectele şi clasele interacţionează şi îşi distribuie responsabilităţile.
b) domeniu de aplicare: şabloanele se pot aplica obiectelor sau claselor.
o şabloanele obiectelor se referă la relaţiile dintre obiecte, relaţii care au un
caracter dinamic.
şabloanele creaţionale ale obiectelor acoperă situaţiile în care o parte
din procesul creării unui obiect cade în sarcina unui alt obiect.
şabloanele structurale ale obiectelor descriu căile prin care se
asamblează obiecte.
şabloanele comportamentale ale obiectelor descriu modul în care un
grup de obiecte cooperează pentru a îndeplini o sarcină ce nu ar putea fi
efectuată de un singur obiect.
o şabloanele claselor se referă la relaţii dintre clase, relaţii stabilite prin
moştenire şi care sunt statice (fixate la compilare).
şabloanele creaţionale ale claselor acoperă situaţiile în care o parte din
procesul creării unui obiect cade în sarcina subclaselor.
şabloanele structurale ale claselor descriu modul de utilizare a
moştenirii în scopul compunerii claselor.
şabloanele comportamentale ale claselor utilizează moştenirea pentru
descrierea unor algoritmi şi fluxuri de control.
În tabelul de mai jos sunt incluse cele mai importante şabloane, clasificate după
criteriile enumerate anterior:
2
Inginerie Software
Scop Creaţionale Structurale Comportamentale
Domeniu de aplicare
Factory Method Adapter (class) Interpreter
Clasa *Interface Template Method
*Marker Interface
*Immutable *Delegation Chain of
Abstract Factory Adapter (object) Responsibility
Builder Bridge Command
Prototype Composite Iterator
Singleton Decorator Mediator
Obiect
Facade Memento
Flyweight Observer
Proxy State
Strategy
Visitor
Observaţie: Şabloanele ale căror nume apar precedate de caracterul '*' formează o categorie
de aşa-numite şabloane fundamentale.
4. Probleme de proiectare
4.1 Găsirea obiectelor adecvate
Aşa cum se ştie, un obiect, elementul de bază al unui sistem OO, include atât date, cât
şi metode (operaţii) care operează asupra datelor. Obiectul execută o operaţie în momentul în
care primeşte o cerere (mesaj) de la un client.
Mesajele reprezintă singura cale prin care un obiect este determinat să execute o
operaţie, în timp ce operaţiile sunt singurul mod de a modifica datele interne ale obiectului.
Din cauza acestor restricţii starea internă a obiectului se spune că este încapsulată: ea nu
poate fi accesată direct, iar reprezentarea ei este invizibilă dinspre exteriorul obiectului.
Partea dificilă în proiectarea unui sistem OO este descompunerea sistemului în
obiecte. Aceasta deoarece procesul este influenţat de mai mulţi factori care acţionează adesea
în mod contradictoriu: încapsularea, granularitatea, dependenţele, flexibilitatea,
performanţele, evoluţia, gradul de reutilizare, etc.
Multe din obiectele ce apar în etapa de proiectare provin din modelul creat în faza de
analiză. Pe parcursul proiectului se vor adaugă şi clase ce nu au corespondenţe în lumea reală.
Unele din aceste clase sunt de nivel primar (de exemplu tablourile). Altele au un nivel de
abstractizare mai ridicat. Şablonul Composite introduce o abstracţiune menită să asigure
tratarea uniformă a obiectelor ce nu au un corespondent fizic.
Modelarea strictă a lumii reale va duce la un sistem ce reflectă realitatea curentă, dar
nu neapărat şi pe cea viitoare. Abstracţiunile identificate în timpul proiectării sunt esenţiale în
obţinerea unui sistem flexibil. Şabloanele ne pot ajuta în identificarea unor abstracţiuni mai
puţin evidente şi a obiectelor care le pot reprezenta. De exemplu, obiectele ce reprezintă
procese sau algoritmi nu apar în natură, dar ele nu pot lipsi dintr-un proiect. Şablonul Strategy
descrie modul de implementare a unor familii interschimbabile de algoritmi. Şablonul State
reprezintă fiecare stare a unei entităţi sub forma unui obiect. Asemenea obiecte sunt rareori
descoperite în timpul analizei sau chiar a stadiului incipient al proiectării.
3
Inginerie Software
4.2 Determinarea granularităţii obiectelor
Obiectele ce compun un sistem pot varia foarte mult atât ca dimensiune cât şi ca
număr. Ele pot reprezenta practic orice: de la componente hardware până la aplicaţii întregi.
Există şabloane care acoperă şi acest aspect. Astfel, şablonul Facade descrie modul în
care subsisteme complete pot fi reprezentate ca obiecte, iar şablonul Flyweight arată cum se
poate gestiona un număr uriaş de obiecte la nivelele cele mai fine de granularitate. Alte
şabloane descriu căile prin care un obiect poate fi descompus în obiecte mai mici.
Abstract Factory şi Builder reprezintă obiecte a căror unică responsabilitate este
crearea de alte obiecte.
Visitor şi Command reprezintă obiecte a căror unică responsabilitate este
implementarea unui mesaj către alt obiect sau grup de obiecte.
4.3 Specificarea interfeţelor obiectelor
Pentru fiecare operaţie declarată într-un obiect se precizează numele, obiectele pe care
le ia ca parametri şi valoarea returnată; aceste elemente formează semnătura operaţiei.
Mulţimea tuturor semnăturilor corespunzătoare operaţiilor dintr-un obiect reprezintă interfaţa
obiectului. Interfaţa unui obiect descrie complet mulţimea mesajelor ce pot fi trimise spre
obiectul respectiv.
Un tip este un nume utilizat pentru a referi o anumită interfaţă. Astfel, vom spune
despre un obiect că este de tipul Window dacă el acceptă toate mesajele corespunzătoare
operaţiilor definite în interfaţa numită Window. Prin urmare, un obiect poate avea mai multe
tipuri, adică o parte a interfeţei sale poate fi de un tip, iar altă parte - de alt tip. De asemenea,
mai multe obiecte pot partaja un anumit tip comun, dacă interfeţele lor includ tipul respectiv.
Interfeţele pot să conţină, la rândul lor, alte interfeţe ca submulţimi. Având două tipuri, T1 şi
T2, vom spune că T1 este subtip al lui T2 dacă interfaţa T1 include interfaţa T2. În acest caz
T2 este supertip al lui T1. Mai spunem că T1 moşteneşte interfaţa T2.
Interfeţele sunt lucruri fundamentale în sistemele OO. Obiectele sunt cunoscute doar
prin intermediul interfeţelor lor. O interfaţă nu dă nici un detaliu relativ la implementarea unui
obiect, iar obiecte distincte pot implementa în mod diferit aceeaşi cerere, iar două obiecte
având implementări complet diferite pot avea interfeţe identice.
Când o cerere este trimisă unui obiect, operaţia ce se va executa depinde de obiectul ce
recepţionează cererea.
Obiecte diferite ce recepţionează cereri identice pot avea implementări diferite ale
operaţiilor ce vor satisface cererile respective. Asocierea unei cereri cu un obiect şi cu o
operaţie a obiectului la momentul execuţiei se numeşte asociere (legare) dinamică (dynamic
binding). Asocierea dinamică permite scrierea de programe în care:
la emiterea unei cereri să nu ne preocupe ce obiect o va recepţiona, ştiindu-se că orice
obiect a cărui interfaţă include o semnătură potrivită va fi bun
obiecte având interfeţe identice pot fi substituite unul altuia, la execuţie; această
posibilitate de substituire se mai numeşte polimorfism.
Polimorfismul este un concept esenţial în cadrul tehnologiei orientate pe obiecte. El
permite:
4
Inginerie Software
ca un obiect client să nu aibă nevoie să cunoască altceva despre alte obiecte decât că
posedă o anumită interfaţă
simplificarea definiţiei clienţilor
decuplarea obiectelor unele de altele
ca în timpul execuţiei obiectele să-si modifice relaţiile dintre ele.
În acest context, şabloanele de proiectare ne ajută la:
definirea interfeţelor
identificarea elementelor care NU trebuie să apară într-o interfaţă.
Astfel, şablonul Memento descrie modul de încapsulare şi salvare a stării interne a
unui obiect, astfel încât starea respectivă să poată fi restaurată ulterior.
Şabloanele specifică de asemenea şi relaţii între interfeţe.
4.4 Specificarea implementării obiectelor
Implementarea unui obiect este definită prin intermediul clasei obiectului. Clasa unui
obiect specifică datele interne ale obiectului şi definiţiile operaţiilor pe care acesta le poate
executa.
Obiectele sunt create prin instanţierea unei clase; se mai spune că un obiect este o
instanţă a unei clase. Procesul de instanţiere a unei clase presupune alocarea de memorie
pentru datele interne ale obiectului respectiv şi asocierea operaţiilor cu aceste date. O clasă
poate fi instanţiată de mai multe ori, în felul acesta rezultând mai multe exemplare similare de
obiecte.
Pe baza unor clase existente se pot defini noi clase, folosind moştenirea claselor. O
subclasă moşteneşte de la una sau mai multe clase părinte (superclase) toate datele şi
operaţiile definite în acestea din urmă. Obiectele instanţe ale subclasei vor
• conţine toate datele definite în subclasa şi în clasele părinte
• putea executa toate operaţiile definite în subclasa şi în clasele părinte.
O clasă abstractă are drept scop principal definirea unei interfeţe comune pentru
subclasele sale. Implementarea operaţiilor unei clase abstracte este "pasată" parţial sau în
întregime subclaselor sale. De aceea, o clasă abstractă nu poate fi instanţiată. Operaţiile
declarate într-o clasă abstractă, dar neimplementate se numesc operaţii abstracte.
Clasele care nu sunt abstracte se numesc clase concrete.
O subclasă poate detalia sau redefini comportamentul claselor părinte. Mai precis,
subclasa poate redefini (override) o operaţie care apare şi într-o clasă părinte, ceea ce permite
subclasei să poată prelua cereri în locul superclasei.
O clasa mixtă este o clasă ce are drept scop oferirea unei interfeţe sau a unei
functionalităţi opţionale altor clase. Ea este similară unei clase abstracte, în sensul că nu poate
fi instanţiată, dar nu poate figura singură ca părinte al unor subclase, ci doar într-o schemă de
moştenire multiplă.
5
Inginerie Software
5. Moştenirea claselor vs Moştenirea interfeţelor
Este foarte important să înţelegem diferenţa dintre
• clasa unui obiect şi
• tipul obiectului.
Clasa defineşte cum este implementat obiectul = starea lui internă + implementarea
operaţiilor.
Tipul se referă doar la interfaţa obiectului = mulţimea mesajelor la care obiectul poate
reacţiona.
Un obiect poate avea mai multe tipuri, iar obiecte ale unor clase diferite pot avea acelaşi
tip. Desigur că între clasă şi tip există o strânsă legătură: prin faptul că o clasă defineşte
operaţiile pe care un obiect le poate executa, automat ea defineşte şi tipul obiectului. Când
spunem că un obiect este instanţă a unei clase, aceasta înseamnă că obiectul posedă interfaţa
definită de clasa respectivă.
Este de asemenea important să înţelegem diferenţa dintre
• moştenirea de clasă şi
• mostenirea de interfaţă (subtipizare).
Moştenirea de clasă presupune că implementarea unui obiect este definită în termenii
implementării altui obiect. Cu alte cuvinte, ea reprezintă un mecanism de reutilizare
(partajare) a reprezentării şi a codului.
Moştenirea de interfaţă este un mecanism prin care un obiect poate fi utilizat în locul
altuia.
Aceste concepte pot fi foarte uşor confundate între ele deoarece majoritatea limbajelor de
programare OO nu le disting în mod explicit. De exemplu, în C++ moştenire înseamnă atât
moştenire de clasă, cât şi de interfaţă. O diferenţiere între cele două s-ar putea face astfel:
• moştenirea de interfaţă poate fi redată ca o derivare publică a unei clase abstracte (o
clasă ce conţine funcţii-membru pur virtuale)
• moştenirea de clasă poate fi modelată prin derivarea privată a unei clase.
Multe dintre şabloanele de proiectare se bazează pe această distincţie. De exemplu,
obiectele dintr-un Chain of Responsibility trebuie să aibă un tip comun, fără însă a avea şi
implementarea comună. În cadrul şablonului Composite, Component defineşte o interfaţă
comună, în timp ce Composite defineşte o implementare comună. Şabloanele Command,
Observer, State şi Strategy sunt adesea implementate cu ajutorul claselor abstracte.
6. Programarea prin interfeţe şi nu prin implementări
Moştenirea de clasă este în esenţă un mecanism ce permite:
• extinderea funcţionalităţii unei aplicaţii, prin reutilizarea funcţionalităţii din clasele
părinte
• definirea rapidă a unui nou fel de obiect, în termenii unuia deja existent
• obţinerea unor noi implementări aproape "pe gratis", preluând o mare parte din ceea ce
avem nevoie de la clasele existente.
6
Inginerie Software
Totuşi, reutilizarea implementării reprezintă doar o faţetă a conceptului de moştenire.
Posibilitatea de a defini familii de obiecte cu interfeţe identice (de obicei prin moştenirea de la
o clasă abstractă) este un alt aspect important, deoarece polimorfismul depinde de el.
Clasele derivate dintr-o clasă abstractă vor partaja interfaţa acelei clase. Subclasele vor
adăuga sau vor redefini operaţii, dar nu vor ascunde operaţii ale clasei părinte. În felul acesta,
toate subclasele vor putea răspunde la cererile corespunzătoare interfeţei clasei abstracte
părinte.
Există două avantaje ale manipulării obiectelor prin intermediul interfeţelor definite în
clasele abstracte:
• clienţii nu trebuie să posede cunoştinţe despre tipurile particulare ale obiectelor
utilizate, atâta timp cât obiectele respective sunt "compatibile" cu intefaţa pe care
clienţii o aşteaptă
• clienţii nu trebuie să ştie care sunt clasele care implementează obiectele respective;
ştiu doar despre clasele abstracte care definesc interfaţa.
Toate acestea reduc substanţial dependenţele dintre subsisteme, permiţând formularea
următorului principiu al proiectării OO:
Programaţi în termeni de interfeţe, nu de implementări.
Printre altele, aceasta înseamnă că nu se recomandă declararea de variabile ale unor clase
concrete, ci folosirea de referinţe ale interfeţelor definite prin clase abstracte. Pe de altă parte,
atunci cănd este necesară instanţierea unor clase concrete, se recomandă aplicarea şabloanelor
creaţionale ce permit abstractizarea procesului de creare a obiectelor. În felul acesta se
realizează o asociere a unei interfeţe cu implementările ei, transparentă la momentul
instanţierii.
7. Mecanisme ale reutilizării
7.1 Moştenire vs Compunerea obiectelor
Cele mai cunoscute tehnici de reutilizare a funcţionalităţii în cadrul sistemelor OO sunt:
• moştenirea de clasă
• asamblarea sau compunerea obiectelor (object composition).
Moştenirea de clasă mai este cunoscută sub numele de reutilizare tip "cutie albă" (white-
box reuse), deoarece în majoritatea cazurilor o parte din starea internă a claselor părinte
este vizibilă în subclase.
Asamblarea obiectelor reprezintă o tehnică de obţinere a unor funcţii noi prin
compunerea unor obiecte având interfeţe bine definite. Tehnica mai este cunoscută sub
numele de reutilizare tip "cutie neagră" (black-box reuse), deoarece obiectele care se
asamblează nu îşi cunosc unul altuia starea internă (ele apar unul faţă de altul ca nişte cutii
negre).
Ambele tehnici au avantaje si dezavantaje.
Moştenirea de clasă se caracterizează prin următoarele:
este definită static, la compilare şi poate fi specificată direct, fiind suportată explicit de
limbajele de programare
7
Inginerie Software
permite modificarea uşoară a implementării operaţiilor reutilizate, şi anume într-o
subclasă ce redefineşte o parte din operaţiile clasei părinte pot fi afectate şi operaţii
moştenite, dacă acestea apelează operaţii redefinite. In secventa C++ de mai jos este
ilustrata sintetic aceasta situatie:
class Parent {
//. . .
public:
void Operation1();
void Operation2(); //apeleaza metoda Operation1
};
class Child: public Parent {
//. . .
public:
void Operation1(); //redefineste Operation1
//Operation2 ramane cea mostenita
};
void aFunction( ) {
Parent p;
Child c;
p.Operation2();
c.Operation2();
}
Deoarece Operation2() apelează Operation1(), metoda se va comporta diferit pentru
cele două obiecte p şi c.
implementarea moştenită de la clasele părinte nu poate fi modificată la momentul
execuţiei.
cel mai adesea clasele părinte definesc cel puţin parţial reprezentarea fizică a
subclaselor lor, deci subclasele au acces la detalii ale implementării superclaselor. De
aceea se mai spune că moştenirea de clasă încalcă principiile încapsulării.
modificările aduse implementării unei superclase vor forţa subclasele să se modifice şi
ele. Dependenţele de implementare pot cauza probleme atunci când se încearcă
reutilizarea subclaselor: dacă anumite aspecte ale implementării moştenite nu
corespund necesităţilor aplicaţiei, clasa părinte trebuie rescrisă sau înlocuită. Această
dependenţă limitează flexibilitatea şi, în ultimă instanţă, reutilizarea. O soluţie în acest
caz ar fi aplicarea moştenirii de la clase abstracte, deoarece ele includ implementare în
mai mică masură.
Compunerea obiectelor se caracterizează prin:
se defineşte în mod dinamic, la execuţie, prin faptul că anumite obiecte primesc
referinţe ale altor obiecte.
necesită ca obiectele să-şi respecte unul altuia interfaţa, ceea ce presupune ca
interfeţele să fie proiectate astfel încât să nu împiedice utilizarea unui obiect în
combinaţie cu mai multe tipuri de obiecte. Deoarece obiectele sunt accesate doar prin
intermediul interfeţelor, nu este încălcat principiul încapsulării. În decursul execuţiei
orice obiect poate fi înlocuit cu altul, atâta timp cât obiectele respective au acelaşi tip.
În plus, datorită faptului că şi implementarea unui obiect este scrisă tot în termenii
interfeţelor altor obiecte, dependenţele de implementare vor fi reduse substanţial.
8
Inginerie Software
prin compunerea obiectelor se obţin următoarele efecte asupra unui proiect: clasele
sunt încapsulate şi "concentrate" asupra câte unui singur obiectiv, ceea ce face ca ele,
ca şi ierarhiile lor, să aibă dimensiuni mici şi să fie mai uşor de gestionat. Un proiect
bazat pe compunerea obiectelor se caracterizează printr-un număr mai mare de obiecte
şi un număr mai mic de clase, iar comportarea sistemului va depinde de relaţiile dintre
obiecte, în loc să fie definită de o anumită clasă.
Toate aceste aspecte conduc spre formularea celui de-al doilea principiu al proiectării OO:
Preferaţi compunerea obiectelor moştenirii de clasă.
Ideal ar fi ca reutilizarea să se aplice nu în vederea creării de componente noi, ci în
vederea obţinerii unei funcţionalităţi dorite prin compunerea obiectelor deja existente. În
practică însă aceasta nu se poate realiza 100% deoarece mulţimea de componente disponibile
nu este niciodată destul de cuprinzătoare. De aceea, moştenirea şi compunerea obiectelor
merg "mână în mână".
Experienţa arată că adesea proiectanţii folosesc moştenirea în mod abuziv. De aceea se
recomandă studiul şi aplicarea şabloanelor de proiectare, acestea bazându-se foarte mult pe
compunerea obiectelor.
7.2 Delegarea
Reprezintă o cale de aplicare a principiului compunerii obiectelor. Într-o relaţie de
delegare două obiecte sunt implicate în rezolvarea unei cereri, şi anume: obiectul care
receptează mesajul (delegatorul) deleagă execuţia operaţiei corespunzătoare unui alt obiect -
delegat.
Acest lucru este oarecum similar cu situaţia în care subclasele "pasează" sarcina
execuţiei unor operaţii claselor părinte (este vorba despre operaţiile moştenite şi neredefinite).
Dar, în timp ce clasa părinte a unei subclase rămâne aceeaşi pe toată durata execuţiei, în cazul
delegării, obiectele delegat pot fi schimbate, cu condiţia să aibă aceeaşi interfaţă.
Delegarea este considerată un şablon de proiectare fundamental, pe ea bazându-se foarte
multe din celelalte şabloane (exemplu: State, Visitor, Strategy, Mediator, Chain of
Responsibility, Bridge).
7.3 Moştenire vs Tipurile parametrizate
Tipurile parametrizate reprezintă o tehnică de reutilizare a funcţionalităţii care nu este
neapărat legată de modelul orientării pe obiecte. Ele permit definirea de către utilizatori a unor
tipuri noi, bazate pe alte tipuri care se dau ca parametri. De exemplu, un tip Lista poate fi
parametrizat prin tipul elementelor conţinute.
Printre limbajele ce suportă această tehnică se numară: Ada, Eiffel (prin tipurile generice)
şi C++ (prin template-uri). Tipurile parametrizate sunt amintite aici deoarece reprezintă cea
de-a treia posibilitate de a defini un anumit comportament prin reutilizare (pe lângă moştenire
şi compunerea obiectelor). Analizând comparativ cele 3 tehnici, se poate concluziona:
• compunerea obiectelor permite modificarea în timpul execuţiei a comportamentului,
dar presupune indirectare şi, prin urmare, poate fi mai puţin eficientă.
• moştenirea oferă posibilitatea de a utiliza implementări deja existente ale unor
operaţii, dar şi de a redefini în subclase operaţiile respective.
9
Inginerie Software
• tipurile parametrizate permit modificarea tipurilor pe care o clasă le utilizează, dar, la
fel ca şi mostenirea, sunt precizate la compilare şi nu mai pot fi modificate la execuţie.
Nici unul din şabloanele de proiectare nu se bazează explicit pe tehnica tipurilor
parametrizate, dar acestea pot să apară în cazul implementarii unor şabloane în C++.
7.4 Structuri stabilite la compilare vs Structuri create la execuţie
Structura unui program OO aflat în execuţie aduce foarte puţin cu structura codului.
Aceasta din urmă este "îngheţată" la momentul compilării şi constă dintr-un ansamblu de
clase aflate în relaţii statice de moştenire. Structura la execuţie constă dintr-o reţea de obiecte
aflate în continuă schimbare şi comunicare. Practic, cele două tipuri de structuri sunt aproape
independente între ele. Diferenţa dintre ele poate fi redată în esenţă prin compararea relaţiilor
de agregare şi asociere (sau cunoaştere - acquaintance).
Agregarea presupune că un anumit obiect posedă sau este responsabil faţă de
un alt obiect, implicând faptul că cele două au durata de viaţă comună.
Asocierea, numită şi relaţie de utilizare (de tip "using") presupune ca un
obiect pur şi simplu "are cunoştinţă" de existenţa altui obiect. Cele două pot
primi mesaje unul de la altul, dar nu sunt responsabile unul faţă de altul.
Asocierea este o relaţie mai slabă decât agregarea.
Cele 2 tipuri de relaţii pot fi uşor confundate din cauză că pot fi implementate în mod
asemănător. De exemplu, în C++ se utilizează de obicei pointerii ca date membru pentru a
stabili legătura între obiecte.
De fapt agregarea şi asocierea sunt determinate mai mult de intenţia proiectantului
decât de mecanismele de limbaj. Distincţia între ele este dificil de observat în codul sursă.
Agregările apar în număr mai mic, dar au un caracter mai stabil în timp. Asocierile, din
contra, se fac şi se refac mai frecvent, uneori stabilindu-se doar pe durata unei operaţii.
Asocierile au un caracter mai dinamic, ceea ce le face greu de depistat în cadrul codului sursă.
Multe dintre şabloanele de proiectare, mai ales cele care au domeniul de aplicare la nivel de
obiect, captează distincţia dintre structurile stabilite la compilare şi cele de la execuţie, în
sensul că un specialist care cunoaşte şabloanele de proiectare poate detecta mai uşor în cadrul
codului sursă structurile ce se vor crea la execuţie.
10