Principii de Oop

13
Şabloane pentru proiectare (Design Patterns) 1. Introducere Proiectarea orientată pe obiecte a software-ului presupune identificarea de obiecte, abstractizarea lor în clase de granularitate potrivită, definirea interfeţelor şi ierarhiilor de moştenire, stabilirea relaţiilor între aceste clase. Soluţia trebuie să rezolve problema şi să fie în acelaşi timp suficient de flexibilă pentru a rezista la noi cerinţe şi probleme ce pot apare în timp. Proiectanţii cu experienţă refolosesc soluţiile bune de câte ori au ocazia. Există grupări de clase sau obiecte care se repetă în cele mai diferite sisteme. Acestea rezolvă probleme specifice, folosirea lor fac proiectele mai flexibile, mai elegante, reutilizabile. Un proiectant care stăpâneşte un set de asemenea şabloane le poate aplica imediat la noile proiecte fără a mai fi nevoit să le redescopere. Şabloanele ce se pot refolosi pot fi general valabile sau specifice unui domeniu, de exemplu pentru probleme de concurenţă, sisteme distribuite, programare în timp real, etc. 2. Clasificare Şabloanele utilizate în sistemele OO pot fi clasificate într-o ierarhie după cum urmează: Idiomuri Mecanisme Cadre (frameworks). Un idiom este legat de un anumit limbaj de programare şi reprezintă o convenţie general acceptată de utilizare a limbajului respectiv. Exemple tipice de idiomuri pot fi găsite în cadrul limbajelor C/C++. Returnarea unei valori întregi care să semnifice succesul sau eşecul unei funcţii este un idiom din C (adoptat şi de C++, pe langă generarea excepţiilor). Importanţa acestui idiom constă în faptul că el reprezintă un anumit stil acceptat de comunitatea utilizatorilor de C şi orice programator care citeşte o secvenţă C recunoaşte imediat această convenţie. Încălcarea acestui idiom are drept consecinţă producerea de cod greu de înţeles, chiar dacă este corect. Practic fiecare limbaj de programare îşi are propriile sale idiomuri. La fel şi o echipă de programatori îşi poate stabili un set de obiceiuri, în funcţie de experienţa şi cultura pe care le posedă. Se poate spune că un idiom este o formă de reutilizare pe scară mică. Un mecanism este o structură în cadrul căruia obiectele colaborează în vederea obţinerii unui anumit comportament ce satisface o anumită cerinţă a problemei.

description

poo

Transcript of Principii de Oop

Şabloane pentru proiectare (Design Patterns)

1. Introducere

Proiectarea orientată pe obiecte a software-ului presupune identificarea de obiecte,

abstractizarea lor în clase de granularitate potrivită, definirea interfeţelor şi ierarhiilor

de moştenire, stabilirea relaţiilor între aceste clase.

Soluţia trebuie să rezolve problema şi să fie în acelaşi timp suficient de flexibilă

pentru a rezista la noi cerinţe şi probleme ce pot apare în timp.

Proiectanţii cu experienţă refolosesc soluţiile bune de câte ori au ocazia. Există

grupări de clase sau obiecte care se repetă în cele mai diferite sisteme. Acestea

rezolvă probleme specifice, folosirea lor fac proiectele mai flexibile, mai elegante,

reutilizabile. Un proiectant care stăpâneşte un set de asemenea şabloane le poate

aplica imediat la noile proiecte fără a mai fi nevoit să le redescopere.

Şabloanele ce se pot refolosi pot fi general valabile sau specifice unui domeniu, de

exemplu pentru probleme de concurenţă, sisteme distribuite, programare în timp real,

etc.

2. Clasificare

Şabloanele utilizate în sistemele OO pot fi clasificate într-o ierarhie după cum

urmează:

Idiomuri

Mecanisme

Cadre (frameworks).

Un idiom este legat de un anumit limbaj de programare şi reprezintă o convenţie

general acceptată de utilizare a limbajului respectiv.

Exemple tipice de idiomuri pot fi găsite în cadrul limbajelor C/C++. Returnarea unei

valori întregi care să semnifice succesul sau eşecul unei funcţii este un idiom din C

(adoptat şi de C++, pe langă generarea excepţiilor). Importanţa acestui idiom constă

în faptul că el reprezintă un anumit stil acceptat de comunitatea utilizatorilor de C şi

orice programator care citeşte o secvenţă C recunoaşte imediat această convenţie.

Încălcarea acestui idiom are drept consecinţă producerea de cod greu de înţeles, chiar

dacă este corect. Practic fiecare limbaj de programare îşi are propriile sale idiomuri.

La fel şi o echipă de programatori îşi poate stabili un set de obiceiuri, în funcţie de

experienţa şi cultura pe care le posedă.

Se poate spune că un idiom este o formă de reutilizare pe scară mică.

Un mecanism este o structură în cadrul căruia obiectele colaborează în vederea

obţinerii unui anumit comportament ce satisface o anumită cerinţă a problemei.

Mecanismele reprezintă decizii de proiectare privind modul în care cooperează

colecţiile de obiecte. Ele se mai numesc şi sabloane de proiectare (design patterns).

Majoritatea sistemelor OO includ mecanisme referitoare la:

persistenţa obiectelor

controlul stocării

controlul proceselor

transmisia/recepţia mesajelor

distribuirea şi migrarea obiectelor

conectarea în reţele (networking)

tranzacţii

evenimente

modul de prezentare ("look & feel") al aplicaţiei.

Un cadru reprezintă o colecţie de clase care oferă un set de servicii pentru un

domeniu particular. Cadrul exportă un număr de clase şi mecanisme pe care

utilizatorii le pot adapta.

Cadrele sunt forme de reutilizare pe scară largă.

Cele mai răspândite tipuri de cadre sunt cele destinate creării de interfeţe grafice.

3. Definiţie

Un sablon 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

OO (orientate pe obiecte) 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 sabloane pentru proiectarea clădirilor şi a oraselor. 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 care 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 sablon este aceeaşi, adică

de soluţie a unei probleme într-un context dat.

Şabloanele de proiectare sunt o memorare pentru posteritate a experienţei în domeniul

proiectării sistemelor OO.

Un şablon este descris de patru elemente:

Nume: foloseşte pentru identificare; descrie sintetic problema rezolvată de

şablon şi soluţia.

Problema: descrie când se aplică şablonul; se descrie problema şi contextul.

Solutia: descrie elementele care intră în rezolvare, relaţiile între ele,

responsabilităţile lor şi colaborările între ele.

Consecinţe si compromisuri: implicaţiile folosirii şablonului, costuri şi

beneficii. 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.

4. Organizarea unui catalog de şabloane

Şabloanele sunt la diferite niveluri de abstractizare, au granularităţi diferite.

Deoarece există multe şabloane de proiectare este necesară o anumită clasificare a lor

în vederea alcătuirii unui catalog.

Criterii de clasificare:

Scop - şabloanele pot fi creaţionale, structurale sau comportamentale.

o Şabloanele creaţionale (creational patterns) privesc modul de creare al

obiectelor.

o Şabloanele structurale (structural patterns) se referă la compoziţia

claselor sau al obiectelor.

o Şabloanele comportamentale (behavioral patterns) caracterizează

modul în care obiectele şi clasele interacţionează şi îşi distribuie

responsabilităţile.

Domeniu de aplicare - şabloanele se pot aplica obiectelor sau claselor.

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.

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.

În tabelul de mai jos sunt incluse cele mai importante şabloane, clasificate după

criteriile enumerate anterior:

Scop

Domeniu de

aplicare

Creationale

Structurale

Comportamentale

Clasa

Factory Method Adapter (clasa)

Interface

Marker Interface

Interpreter

Template Method

Obiect

Immutable

Abstract Factory

Builder

Prototype

Singleton

Delegation

Adapter (obiect)

Bridge

Composite

Decorator

Facade

Flyweight

Proxy

Chain of Responsibility

Command

Iterator

Mediator

Memento

Observer

State

Strategy

Visitor

5. Rolul şabloanele de proiectare în rezolvarea problemele de proiectare

Şabloanele de proiectare rezolvă multe din problemele cu care se confruntă

proiectanţii. Câteva din aceste probleme sunt urmatoarele:

5.1 Găsirea obiectelor adecvate

Un obiect, care intră în alcătuirea programelor OO, împachetează atât date, cât şi

metode (operaţii) ce operează asupra datelor. Obiectul execută o operaţie când

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.

Există mai multe metodologii OO: unele se concentrează pe substantivele şi verbele

extrase din enunţul problemei, altele se concentrează pe colaborările şi

responsabilităţile din sistem sau se modelează lumea reală şi obiectele găsite la

analiză se translatează şi în proiectare.

Multe din obiectele care apar la proiectare provin din modelul creat în faza de analiză.

La proiect se vor adauga însă şi clase care nu au corespondenţă în lumea reală. Unele

din aceste clase sunt de nivel primar (de exemplu tablourile), altele au un nivel de

abstractizare mai ridicat. De exemplu sablonul Composition introduce o abstracţie

menită să asigure tratarea uniformă a obiectelor care 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 care 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.

5.2 Determinarea granularităţii obiectelor

Obiectele ce compun un sistem pot varia enorm ca mărime şi ca număr. Ele pot

reprezenta practic orice începând de la componente hardware până la aplicaţii întregi.

Cum decidem ce ar trebui sa fie un obiect?

Există şabloane care acoperă şi acest aspect. Astfel, şablonul Facade descrie modul în

care subsisteme complete pot fi reprezentate ca obiecte, şablonul Flyweight arată cum

se poate gestiona un număr uriaş de obiecte la nivelurile 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.

5.3 Specificarea interfeţelor obiectelor

Pentru fiecare operaţie declarată într-un obiect se precizează numele, obiectele pe

care le ia ca parametrii ş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 setul mesajelor

care 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. Ca 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, daca

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 ca T1 moşteneşte interfaţa T2.

Interfeţele sunt lucruri fundamentale în sistemele OO. Obiectele sunt cunoscute doar

prin intermediul interfetelor lor. O interfaţă nu dă nici un detaliu relativ la

implementarea unui obiect, iar obiecte distincte pot implementa în mod diferit o

aceeasi cerere. Altfel spus, două obiecte având implementări complet diferite pot avea

interfeţe identice.

Când o cerere este trimisă unui obiect, operaţia care se va executa depinde de:

cerere

obiectul care receptionează cererea.

Obiecte diferite care pot recepţiona 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ătura potrivită va fi bun;

obiecte având interfeţe identice pot fi substituite unul altuia la executie;

această posibilitate de substituire se mai numeşte polimorfism.

Polimorfismul este un concept esenţial în cadrul tehnologiei orientate pe obiecte.

El permite:

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 la execuţie obiectele să-şi 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 pentru ca starea respectivă să poată fi restaurată ulterior. Şabloanele

specifică de asemenea şi relaţii între interfeţe.

5.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 mixin este o clasă care are drept scop oferirea unei interfeţe sau a unei

funcţionalităţ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.4.1 Moştenirea claselor şi moştenirea interfeţelor

Este important să inţelegem diferenţa între clasa unui obiect şi tipul obiectului.

Clasa defineşte starea internă a obiectului şi implementarea operaţiilor lui. Tipul

obiectului se referă doar la interfaţa obiectului - setul cererilor la care obiectul poate

răspunde. Un obiect poate avea mai multe tipuri, iar obiecte de clase diferite pot avea

acelaşi tip.

Desigur că între clasă şi tip există o strânsă legătura: 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ă.

Un limbaj ca C++ foloseşte clasele pentru a specifica tipul obiectului şi

implementarea.

Este de asemenea important să inţelegem diferenţa dintre moştenirea de clasă şi

moştenirea 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ţă (subtyping) este un mecanism prin care un obiect poate fi

utilizat în locul altuia.

Este destul de uşor de confundat aceste concepte î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ă care 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.

5.4.2 Programarea prin interfeţe şi nu prin implementări

Moştenirea de clasă este în esenţă un mecanism care 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 fără efort, prin preluarea unei mari

părţi din ceea ce avem nevoie de la clase existente.

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ă doua avantaje ale manipulării obiectelor prin intermediul interfeţelor definite în

clasele abstracte:

clienţii nu trebuie să ştie tipurile particulare ale obiectelor utilizate, atâta timp

cât obiectele respective sunt compatibile cu interfaţ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, permitând formularea

următorului principiu al proiectării OO:

PROGRAMAŢI ÎN TERMENI DE INTERFEŢE, NU DE IMPLEMENTĂRI.

Nu declara variabile ca fiind instanţe ale unor clase concrete, mai degrabă angajează-

te să foloseşti o interfaţă definită printr-o clasă abstractă. Când este necesară

instanţierea unor clase concrete, se recomandă aplicarea şabloanelor creaţionale

(Abstract Factory, Builder, Factory Method, Prototype, Singleton) care 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.

5.5 Mecanisme ale reutilizării

Problema este cum obţinem un software flexibil şi reutilizabil folosind concepte ca

obiect, clasă, interfaţă, moştenire. Şabloanele constituie un răspuns.

Moştenirea şi compunerea obiectelor

Cele mai cunoscute tehnici de reutilizare a funcţionalităţii în cadrul sistemelor OO

sunt moştenirea de clasă şi asamblarea sau compunerea obiectelor (object

composition).

Moştenirea de clasă este cunoscută şi 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 unei funcţionalităţi noi prin

asamblarea sau compunerea unor obiecte având interfeţe bine definite. Tehnica 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 şi dezavantaje. Moştenirea de clasă se caracterizează prin

următoarele (avantaje):

este definită static la compilare şi poate fi specificată direct, fiind suportată

explicit de limbajele de programare;

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. În secvenţa C++

de mai jos este ilustrată sintetic această situaţie:

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 apeleaza Operation1, metoda se va comporta

//diferit pentru cele 2 obiecte

}

Dezavantaje:

implementarea moştenită de la clasele părinte nu poate fi modificată în

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 mică măsură.

Compunerea obiectelor se caracterizează prin:

se defineste î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 că

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 substanţial reduse;

prin compunerea obiectelor se obţine urmatorul efect asupra unui proiect:

clasele sunt încapsulate şi focalizate 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ă într-o

clasă.

Toate aceste aspecte conduc spre formularea celui de-al doilea principiu al proiectării

OO:

PREFERAŢI COMPUNEREA OBIECTELOR MOŞTENIRII DE CLASĂ.

În mod ideal nu ar trebui create noi componente pentru a realiza reutilizarea. Pentru

obţinerea funcţionalităţii dorite trebuie doar asamblate componentele prin intermediul

compoziţiei obiectelor existente. În practică însă aceasta nu se poate realiza în

totalitate deoarece setul de componente disponibile nu este niciodată destul de bogat.

Reutilizarea prin moştenire este mai uşor de folosit pentru a face componente noi în

comparaţie cu compunerea componentelor deja existente. De aceea moştenirea şi

compunerea obiectelor sunt folosite împreună.

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.

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ă ca un şablon de proiectare fundamental, pe ea bazându-se

foarte multe din celelalte şabloane (de exemplu State, Visitor, Strategy, Mediator,

Chain of Responsibility, Bridge).

Principalul avantaj al delegării este că face posibil foarte uşor să se compună

comportari în timpul execuţiei, inclusiv să se schimbe dinamic această compunere.

Dezavantajul, pe care îl au şi alte tehnici ce fac software-ul flexibil, este că software-

ul dinamic, parametrizat, este mai greu de înţeles decât software-ul static. În plus

există şi penalităţi în timpul execuţiei.

Moştenirea şi tipurile parametrizate

Tipurile parametrizate reprezintă o altă tehnică de reutilizare a funcţionalităţii care nu

este strict legată de modelul orientat pe obiecte. Ele permit definirea de către

utilizatori a unui tip nou fără a specifica tipurile pe care acesta le foloseşte. Aceste

tipuri se vor furniza ca şi parametrii unde se foloseşte acest tip parametrizat. De

exemplu un tip Lista poate fi parametrizat prin tipul elementelor conţinute. Acesta

poate fi întreg, şir de caractere, etc.

Printre limbajele care suportă această tehnică se numără Ada, Eiffel (prin tipurile

generice) şi C++ (prin template-uri).

Tipurile parametrizate ne oferă a treia cale pentru a compune comportarea în

sistemele OO. Există diferenţe importante între aceste trei tehnici:

compunerea obiectelor permite modificarea în timpul execuţiei a

comportamentului, dar presupune indirectare şi, ca 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;

tipurile parametrizate permit modificarea tipurilor pe care o clasă le

utilizează, dar, la fel ca şi moştenirea, sunt precizate la compilare şi nu mai pot

fi modificate la execuţie.

5.6 Structuri stabilite la compilare şi structuri create la execuţie

Structura unui program OO aflat în execuţie aminteşte foarte puţin cu structura

codului. Acesta din urmă este îngheţat în momentul compilării şi constă dintr-un

ansamblu de clase aflate în relaţii fixe de moştenire. Structura la execuţie constă dintr-

o reţea de obiecte aflate în continuă schimbare şi comunicare. De fapt cele două tipuri

de structuri sunt aproape independente intre ele.

Exemplificăm pe deosebirea dintre relaţiile de agregare şi asociere (sau cunoaştere -

acquaintance) şi cum se manifestă acestea în timpul compilării şi execuţiei. Agregarea

presupune ca un anumit obiect posedă sau este responsabil faţă de un alt obiect. În

general spunem despre un obiect că are sau este parte dintr-un alt obiect. Agregarea

implică faptul că un obiect agregat şi proprietarul lui au durata de viaţă comună.

Asocierea, numită şi relaţie de utilizare (de tip "using"), presupune că un obiect pur şi

simplu ştie de existenţa altui obiect. Cele două obiecte asociate pot cere operaţii unul

altuia dar nu sunt responsabili unul faţă de altul. Asocierea este o relaţie mai slabă

decât agregarea şi sugerează o cuplare mai slabă între obiecte.

Cele două tipuri de relaţii pot fi uşor confundate din cauză că pot fi implementate în

mod asemănător. În C++ agregarea se poate implementa prin definirea de membrii

variabilă ce sunt instanţe adevărate dar este mai folosită practica să o definim prin

pointeri sau referinţe la instanţe. Asocierea este implementată şi ea prin pointeri şi

referinţe.

De fapt agregarea şi asocierea sunt determinate mai mult de intenţia proiectantului

decât de mecanismele din 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 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

codul 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 codul sursă structurile ce se vor crea la execuţie.

6. Tema

1. Pornind de la introducerea în şabloane de proiectare, daţi exemple de aplicare a

următoarelor relaţii:

agregare

asociere

moştenire de clasă

moştenire de interfaţă

Faceţi o apreciere în termeni de avantaje, dezavantaje şi flexibilitate pentru aceste

relaţii.

2. Sa se defineasca o ierarhie de clase pentru a modela mai multe tipuri de masini cu

motorizarile aferente. Masinile se impart in 2 categorii: automobile si camioane,

fiecare din acestea putand avea un motor Diesel sau pe baza de benzina.

Fiecare masina va avea o marca, o culoare si un numar de kilometrii parcursi pana in

prezent. Motorul are o marca si o serie unica.

Printr-un meniu se va permite realizarea urmatoarelor operatii:

- adaugare masina

- stergere masina

- afisarea masinilor

- schimbare tip motor masina selectata

- actualizare nr. km parcursi pentru masina specificata

Indicatie:

Se are in vedere construirea urmatoarei ierarhii de clase:

- clasa abstracta engine

- clasele derivate din engine -> dieselEngine si gasEngine

- clasa abstracta car

- clasele derivate din car -> automobile si truck

Intre engine si car exista e relatie de agregare.

Sa se discute motivatia ce sta la baza contruirii acestei ierarhii