Rolul Sabloanelor de Proiectare Pentru a Rezolva Probleme de Proiectare
5. Rolul șabloanele de proiectare pentru a rezolva probleme de proiectare
Proiectații se confruntă cu diferite probleme pe care le rezolva șabloanele de proiectare. Dintre aceste probleme putem enumera:
5.1 Găsirea obiectelor adecvate
În alcătuirea programelor OO, un obiect, împachetează atât date, precum și metode (operații) care operează asupra datelor. Astfel, obiectul execută o operație când primește o cerere (mesaj) de la un client.
Mesajele sunt singura cale astfel încât un obiect este determinat să execute o operație, în timp ce operațiile fiind singurul mod de a modifica datele interne ale obiectului. Potrivit acestor restricții starea internă a obiectului este încapsulată: acesta neputând fi accesată direct, iar reprezentarea ei fiind invizibilă dinspre exteriorul obiectului.
O parte dificilă în proiectarea unui sistem OO este împărțirea sistemului în obiecte. Deoarece procesul este influențat de mai mulți factori care acționează adesea în mod contradictoriu: încapsularea, flexibilitatea, gradul de reutilizare, granularitatea, performanțele, dependențele,evoluția.
Există mai multe metodologii OO: unele se concentrează pe verbele și substantivele extrase din enunțul problemei, altele se concentrează pe responsabilitățile și colaborările 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:
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:
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:
Î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.
Interconectarea/ Comunicarea dintre technologii
https://www.google.ro/?gws_rd=cr,ssl&ei=9QB-VdLED6v8ygPqmKfQCw#q=wiki+windows+azure
https://en.wikipedia.org/wiki/Microsoft_Azure
https://en.wikipedia.org/wiki/Microsoft_Azure#SQL_Database
https://en.wikipedia.org/wiki/Active_Server_Pages
http://www.cs.ubbcluj.ro/~vcioban/Bistrita/Manuale/CursDotNetSassu.pdf am modificat la C#
https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller
https://ro.wikipedia.org/wiki/Windows_Communication_Foundation
http://thor.info.uaic.ro/~iasimin/Special/WPF_C1_2012.pdf
https://en.wikipedia.org/wiki/Use_Case_Diagram
https://en.wikipedia.org/wiki/Class_diagram
http://www.microsoft.com/romania/educatie/curs_dot_net/profesori/default.aspx .Net
Copyright Notice
© Licențiada.org respectă drepturile de proprietate intelectuală și așteaptă ca toți utilizatorii să facă același lucru. Dacă consideri că un conținut de pe site încalcă drepturile tale de autor, te rugăm să trimiți o notificare DMCA.
Acest articol: Rolul Sabloanelor de Proiectare Pentru a Rezolva Probleme de Proiectare (ID: 163420)
Dacă considerați că acest conținut vă încalcă drepturile de autor, vă rugăm să depuneți o cerere pe pagina noastră Copyright Takedown.
