510 likes | 719 Views
POO. Proiectarea de clase. Cuprins. principiul inchis-deschis principiul substituirii principiul de inversare a dependentelor sabloane de proiectare (software design patterns) clase cu o singura instanta (Singleton) fabrica de obiecte (Abstract Object Factory). Principiul “inchis-deschis”.
E N D
POO Proiectarea de clase
Cuprins • principiul inchis-deschis • principiul substituirii • principiul de inversare a dependentelor • sabloane de proiectare (software design patterns) • clase cu o singura instanta (Singleton) • fabrica de obiecte (Abstract Object Factory) POO – Proiectarea de clase
Principiul “inchis-deschis” • “Entitatile software (module, clase, functii etc.) trebuie sa fie deschise la extensii si inchise la modificare” (Bertrand Meyer, 1988) • “deschis la extensii” = comportarea modulului poate fi extinsa pentru a satisface noile cerinte • “inchis la modificare” = nu este permisa modificarea codului sursa POO – Proiectarea de clase
Principiul “inchis-deschis”: neconformare void ContainerFig::incarca(std::ifstream& inp) { while (inp) { int tipFig; Figura* pfig; inp >> tipFig; switch (tipFig) { case SEGMID: ... case CERCID: ... } } } • O solutie: Fabrica de figuri (… un pic mai tarziu) POO – Proiectarea de clase
Principiul “inchis-deschis”: conventii • Declara toate datele membre private • altfel: • Schimbarea tipului datei implica schimbarea functiilor care o utilizeaza • in plus, nu avem incapsulare • Fara variabile globale • O alternativa: clase cu membri statici POO – Proiectarea de clase
Principiul substituirii • “Functiile care utilizeaza pointeri sau referinte la clasa de baza trebuie sa fie apte sa utilizeze obiecte ale claselor derivate fara sa le cunoasca” care parafrazeaza • “Daca pentru fiecare obiect o de tip S exista un obiect o’ de tip T astfel incat, pentru toate programele P definite in termenii lui T, comportarea lui P nu se schimba daca substituim o cu o’, atunci S este un subtip al lui T.” (Barbara Liskov, 1988) POO – Proiectarea de clase
Principiul substituirii: neconformare POO – Proiectarea de clase
Principiul substituirii: neconformare class Dreptunghi { public: virtual void dubleazaLatime(); virtual int aria(); protected: int latime, lungime; } Daca dr.aria = d, dupa dr.dubleazaLatime(); avem dr.aria = 2*d class Patrat : public Dreptunghi { public: virtual void dubleazaLatime(int); virtual int aria(); } Daca pat.aria = p, dupa pat.dubleazaLatime(); avem pat.aria = 4*p POO – Proiectarea de clase
Principiul substituirii: neconformare void Drepunghi::dubleazaLatime() { latime *= 2; } void Patrat::dubleazaLatime() { latime *= 2; lungime *= 2; } void g(Dreptunghi& d) { int aria1 = d.aria(); d.dubleazaLatime(); assert(d.aria() = 2*aria1); } POO – Proiectarea de clase
Proiectare prin contract • precizeaza pre- si post-conditiile pentu fiecare metoda • “cand se redefineste o metoda intr-o clasa derivata, preconditia se inlocuieste prin o conditie mai slaba iar postconditia prin una mai tare.” (Bertrand Mayer, 1988) POO – Proiectarea de clase
Proiectare prin contract ...A::f(...) { //requires p1 (prec.) //ensures q1 (post.) ... } ...B::f(...) { //requires p2 (prec.) //ensures q2 (post.) ... } POO – Proiectarea de clase
Proiectare prin contract A a; B b; • b poate fi utilizat oriunde a este utilizat • b.f() poate fi apelata in orice stare in care a.f() este apelata, deci b.f() necesita (requires) o conditie mai slaba • daca p1, atunci p2 • starile produse de b.f() satisfac proprietatile satisfacute de starile corespunzatoare produse de a.f(), deci b.f() asigura (ensures) o conditie mai tare decat a.f() • daca q2, atunci q1 POO – Proiectarea de clase
Proiectare prin contract void Dreptunghi::dubleazaLatime(){ //requires: true //ensures: latime = 2 * old(latime) /\ // lungime = old(lungime) latime *= 2; } void Patrat::dubleazaLatime(){ //requires: lungime = latime //ensures: latime = 2 * old(latime) /\ // lungime = latime latime *= 2; lungime *= 2; } POO – Proiectarea de clase
Principiul substituirii //requires: true //ensures: latime = 2 * old(latime) //requires: lungime = latime //ensures: latime = 2 * old(latime)) /\ // lungime = latime POO – Proiectarea de clase
Principiul de inversare a dependentelor • A. “Modulele de nivel inalt nu trebuie sa depinda de modulele de nivel jos. Amandoua trebuie sa depinda de abstractii.” • B. “Abstractiile nu trebuie sa depinda de detalii. Detaliile trebuie sa depinda de abstractii.” • programele OO bine proiectate inverseaza dependenta structurala de la metoda procedurala traditionala • metoda procedurala: o procedura de nivel inalt apeleaza o procedura de nivel jos, deci depinde de ea POO – Proiectarea de clase
Principiul de inversare a dependentelor POO – Proiectarea de clase
Principiul de inversare a dependentelor class Lampa { public: void aprinde(); void stinge(); } class Buton { public: Buton(Lampa& plampa) : lampa(&plampa) {} void detecteaza(); private: Lampa *lampa; } POO – Proiectarea de clase
Principiul de inversare a dependentelor void Buton::detecteaza() { bool stare = getStareFizica(); if (stare) lampa->aprinde(); else lampa->stinge(); } POO – Proiectarea de clase
Principiul de inversare a dependentelor POO – Proiectarea de clase
Principiul de inversare a dependentelor class ClientButon { public: virtual void aprinde() = 0; virtual void stinge() = 0; } class Buton { public: Buton(ClientButon& pclient) : client(&pclient) {} void detecteaza(); virtual bool getStare() = 0; private: ClientButon *client; } POO – Proiectarea de clase
Principiul de inversare a dependentelor class Lampa : public ClientButon { public: virtual void aprinde(); virtual void stinge(); } class ButonImpl : Buton { public: ButonImpl(ClientButon& pclient) : client(&pclient) {} virtual bool getStare(); private: } POO – Proiectarea de clase
Sabloane de proiectare (Design Patterns) • intai aplicate in proiectare urbanistica: C. Alexander. A Pattern Language. 1977 • prima contributie in software: 1987, Kent Beck (creatorul lui Extreme Programming) & Ward Cunningham (a scris primul wicki) • contributia majora: Design Patterns: Gamma et al. Elements of Reusable Object-Oriented Software was published, 1994 • cunoscuta ca GoF (Gang of Four) • in functie de scop, clasifica patternurile in • creationale • structurale • comportamentale • pot fi aplicate la nivel de clasa sau obiect POO – Proiectarea de clase
Ce este un sablon de proiectare? • definitia originala a lui Alexander: "Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice“ • Elementele esentiale ale unui pattern (GoF): • nume • problema (si context) • solutie • consecinte • GoF include 23 de sabloane POO – Proiectarea de clase
Formatul (template) unui sablon • nume si clasificare • intentie • cunoscut de asemenea ca • motivatie • aplicabilitate • structura • participanti • colaborari • consecinte • implementare • cod • utilizari cunoscute • sabloane cu care are legatura POO – Proiectarea de clase
Clase cu o singura instanta (Singleton) • Intentia • proiectarea unei clase cu un singur obiect (o singura instanta) • Motivatie • intr-un sistem de operare: • exista un sistem de fisiere • exista un singur manager de ferestre • Aplicabilitate • cand trebuie sa existe exact o instanta • clientii clasei trebuie sa aiba acces la instanta din orice punct bine definit POO – Proiectarea de clase
Clase cu o singura instanta (Singleton) • structura • participant: Singleton • colaborari: clientii clasei POO – Proiectarea de clase
Clase cu o singura instanta (Singleton) • Consecinte • acces controla la instanta unica • reducerea spatiului de nume (eliminarea variab. globale) • permite rafinarea operatiilor si reprezentarii • permite un numar variabil de instante • mai flexibila decat operatiile la nivelde clasa (statice) • Implementare POO – Proiectarea de clase
Clase cu o singura instanta (Singleton) POO – Proiectarea de clase
Clase cu o singura instanta class Singleton { public: static Singleton& instance() {return uniqueInstance;} int getValue() { return i; } void setValue(int x) { i = x; } private: static Singleton uniqueInstance; int i; Singleton(int x) : i(x) { } void operator=(Singleton&); Singleton(const Singleton&); }; POO – Proiectarea de clase
Clase cu o singura instanta Singleton Singleton::s(47); int main() { Singleton& s1 = Singleton::instance(); cout << s1.getValue() << endl; Singleton& s2 = Singleton::instance(); s2.setValue(9); cout << s1.getValue() << endl; return 0; } POO – Proiectarea de clase
Fabrica de obiecte (Abstract Factory) • intentie • de a furniza o interfata pentru crearea unei familii de obiecte intercorelate sau dependente fara a specifica clasa lor concreta • aplicabilitate • un sistem ar trebui sa fie independent de modul in care sunt create produsele, compuse sau reprezentate • un sistem ar urma sa fie configurat cu familii multiple de produse • o familie de obiecte intercorelate este proiectata pentru astfel ca obiectele sa fie utilizate impreuna • vrei sa furniziei o biblioteca de produse ai vrei sa accesibila numai interfata, nu si implementarea POO – Proiectarea de clase
Fabrica de obiecte:: motivatie typedef enum {SEGMID = 1, CERCID} TipFig; POO – Proiectarea de clase
Fabrica de obiecte:: motivatie void ContainerFig::incarca(std::ifstream& inp) { while (inp) { // citeste tipul figurii int tipFig; inp >> tipFig; // creeaza un obiect vid Figura* pfig; switch (tipFig) { case SEGMID: pfig = new Segment; break; case CERCID: pfig = new Cerc; break; //... } pfig->citeste(inp); } } POO – Proiectarea de clase
Fabrica de obiecte:: structura POO – Proiectarea de clase
Fabrica de obiecte POO – Proiectarea de clase
Fabrica de obiecte • colaborari • normal se creeaza o singura instanta • Consecinte • izoleaza clasele concrete • simplifica schimbul familiei de produse • promoveaza consistenta printre produse • suporta noi timpul noi familii de produse usor • respecta principiul deschis/inchis • implementare POO – Proiectarea de clase
Functii delegat (callback) • o functie delegat (callback) este o functie care nu este invocata explicit de programator; responsabilitatea apelarii este delegata altei functii care primeste ca parametru adresa functiei delegat POO – Proiectarea de clase
Fabrica de obiecte:: solutia • definim mai intai clasa de baza ca si clasa abstracta class Figura { public: Figura() { std::cout << "Figura();\n"; } virtual void citeste(std::ifstream&) = 0; }; POO – Proiectarea de clase
Fabrica de obiecte:: solutia • definim apoi o fabrica de figuri, adica o clasa care sa gestioneze tipurile de figuri • inregistreaza un nou tip de figura (apelata ori de cate ori se defineste o noua clasa derivata) • eliminarea unui tip de figura inregistrat (stergerea unei clase derivate) • crearea de figuri • la nivel de implementare utilizam perechi (IdTipFig, PointerFunctieDeCreareDelegata) POO – Proiectarea de clase
Fabrica de obiecte:: FabricaDeFigBaza class FabricaDeFigBaza { public: typedef Figura* (*DelegatCreareFig)(); FabricaDeFigBaza() { } bool inregistreazaFig(int figId, \ DelegatCreareFig fctCreare); bool elimina(int figId); Figura* creeazaFig(int figId); private: typedef std::map<int, DelegatCreareFig> DelegatMap; DelegatMap delegati_; }; POO – Proiectarea de clase
Fabrica de obiecte :: FabricaDeFigBaza bool FabricaDeFigBaza::inregistreazaFig(int figId, DelegatCreareFig fctCreare) { return delegati_.insert(DelegatMap::value_type(figId, fctCreare)).second; } bool FabricaDeFigBaza::elimina(int figId) { return delegati_.erase(figId) == 1; } POO – Proiectarea de clase
Fabrica de obiecte :: FabricaDeFigBaza Figura* FabricaDeFigBaza::creeazaFig(int figId) { DelegatMap::const_iterator i = delegati_.find(figId); if (i == delegati_.end()) { // nu a gasit throw "ID de figura necunoscut\n"; } // invoca functia de creare return (i->second)(); } POO – Proiectarea de clase
Fabrica de obiecte:: functia de incarcare FabricaDeFigBaza fabricaFig; // global void ContainerFig::incarca(std::ifstream& inp) { // citeste tipul figurii int figId; inp >> figId; while (inp) { //creeaza un obiect vid Figura* pfig = fabricaFig.creeazaFig(figId); // incarca obiectul pfig->citeste(inp); cntnr.push_back(pfig); // citeste tipul noii figuri inp >> figId; } } POO – Proiectarea de clase
Fabrica de obiecte:: un nou tip de figura class Segment : public Figura { public: Segment() { std::cout << "Segment()\n"; } virtual void citeste(std::ifstream&); }; POO – Proiectarea de clase
Fabrica de obiecte:: inregistrarea unui nou tip namespace { Figura* creeazaSegm() { return new Segment; } // Id-ul pentru segment const int SEGMID = 1; // inregistreaza const bool registeredSEGM = fabricaFig.inregistreazaFig(SEGMID, \ creeazaSegm); } POO – Proiectarea de clase
Fabrica de obiecte:: solutia::demo int main() { try { ContainerFig cfb; std::ifstream inp("inp.dat"); cfb.incarca(inp); return 0; } catch (char* msg) { std::cout << msg; } } POO – Proiectarea de clase
Fabrica de obiecte:: solutia::rafinare • declararea fabricii de figuri ca obiect global nu este o solutie prea eleganta • putem defini in schimb o clasa cu o singura instanta POO – Proiectarea de clase
Fabrica cu o singura instanta class FabricaDeFig : public FabricaDeFigBaza { public: static FabricaDeFig& Instanta() {return fabricaFig;} private: static FabricaDeFig fabricaFig; FabricaDeFig() { } void operator=(FabricaDeFig&); FabricaDeFig(const FabricaDeFig&); }; FabricaDeFig FabricaDeFig::fabricaFig; POO – Proiectarea de clase
Fabrica de obiecte :: incarca (rafinat) void ContainerFig::incarca(std::ifstream& inp) { ... while(inP) { //creeaza un obiect vid Figura* pfig = FabricaDeFig::Instanta().creeazaFig(figId); ... } } POO – Proiectarea de clase
Fabrica de obiecte :: inregistrare (rafinat) namespace { Figura* creeazaSegm() { .... } ... // inregistreaza const bool registeredSEGM = FabricaDeFig::Instanta().inregistreazaFig( SEGMID, creeazaSegm ); } POO – Proiectarea de clase