Transcript POO
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) D. Lucanu POO – Proiectarea de clase 2
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 D. Lucanu POO – Proiectarea de clase 3
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) D. Lucanu POO – Proiectarea de clase 4
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 D. Lucanu POO – Proiectarea de clase 5
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) D. Lucanu POO – Proiectarea de clase 6
Principiul substituirii: neconformare Dreptunghi
dubleazaLatime()
Autovehicul
merge()
Patrat
dubleazaLatime()
AutovehiculCuGabaritDepasit
merge() D. Lucanu POO – Proiectarea de clase 7
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 D. Lucanu POO – Proiectarea de clase 8
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); }
D. Lucanu POO – Proiectarea de clase 9
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) D. Lucanu POO – Proiectarea de clase 10
Proiectare prin contract ...A::f(...) { //requires p1 (prec.) //ensures q1 (post.) ...
}
f()
...B::f(...) { //requires p2 (prec.) //ensures q2 (post.) ...
}
D. Lucanu f() POO – Proiectarea de clase
A B
11
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 D. Lucanu POO – Proiectarea de clase 12
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; }
D. Lucanu POO – Proiectarea de clase 13
Principiul substituirii Dreptunghi
dubleazaLatime()
//requires: true //ensures: latime = 2 * old(latime) Patrat
dubleazaLatime() D. Lucanu
//requires: lungime = latime //ensures: latime = 2 * old(latime)) /\ // lungime = latime
POO – Proiectarea de clase 14
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 D. Lucanu POO – Proiectarea de clase 15
Principiul de inversare a dependentelor
: Top Package::Buton aprinde() stinge() : Top Package::Lampa
Lampa
1 1
Buton
D. Lucanu POO – Proiectarea de clase 16
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; }
D. Lucanu POO – Proiectarea de clase 17
Principiul de inversare a dependentelor void Buton::detecteaza() { bool stare = getStareFizica(); if (stare) lampa->aprinde(); else lampa->stinge(); }
D. Lucanu POO – Proiectarea de clase 18
Principiul de inversare a dependentelor
ClientButon
1 1
Buton
Lampa ButonImplementare
D. Lucanu POO – Proiectarea de clase 19
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; }
D. Lucanu POO – Proiectarea de clase 20
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: }
D. Lucanu POO – Proiectarea de clase 21
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 D. Lucanu POO – Proiectarea de clase 22
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 D. Lucanu POO – Proiectarea de clase 23
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 D. Lucanu POO – Proiectarea de clase 24
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 D. Lucanu POO – Proiectarea de clase 25
Clase cu o singura instanta (Singleton)
structura V isu al P ar ad igm for U M L E nt er al pr P ise -data ar E di
Singleton
participant: Singleton isu tion [ev ar -uniqueInstance +setValue() +getValue() for +instance() ad ad U al M ua igm L tion E for nt co U er pr py M L ise ] E E nt di er pr tion ise E [ev al di tion ua [ev tion co al ua py ] tion co py ] colaborari: clientii clasei D. Lucanu POO – Proiectarea de clase 26
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 D. Lucanu POO – Proiectarea de clase 27
Clase cu o singura instanta (Singleton)
D. Lucanu POO – Proiectarea de clase 28
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&);
D. Lucanu POO – Proiectarea de clase 29
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; }
D. Lucanu POO – Proiectarea de clase 30
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 D. Lucanu POO – Proiectarea de clase 31
Fabrica de obiecte:: motivatie Segment
citeste()
Figura
citeste() 1
Cerc
citeste()
ContainerFig
* incarca()
typedef enum {SEGMID = 1, CERCID} TipFig;
D. Lucanu POO – Proiectarea de clase 32
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); } }
D. Lucanu POO – Proiectarea de clase 33
Fabrica de obiecte:: structura
D. Lucanu POO – Proiectarea de clase 34
Fabrica de obiecte
D. Lucanu POO – Proiectarea de clase 35
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 D. Lucanu POO – Proiectarea de clase 36
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 D. Lucanu POO – Proiectarea de clase 37
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;
D. Lucanu POO – Proiectarea de clase 38
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) D. Lucanu POO – Proiectarea de clase 39
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
D. Lucanu POO – Proiectarea de clase 40
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; }
D. Lucanu POO – Proiectarea de clase 41
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)(); }
D. Lucanu POO – Proiectarea de clase 42
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; } }
D. Lucanu POO – Proiectarea de clase 43
Fabrica de obiecte:: un nou tip de figura class Segment : public Figura { public: Segment() { std::cout << "Segment()\n"; } }; virtual void citeste(std::ifstream&);
D. Lucanu POO – Proiectarea de clase 44
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); }
D. Lucanu POO – Proiectarea de clase 45
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; } }
D. Lucanu POO – Proiectarea de clase 46
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 D. Lucanu POO – Proiectarea de clase 47
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;
D. Lucanu POO – Proiectarea de clase 48
Fabrica de obiecte :: incarca (rafinat) void ContainerFig::incarca(std::ifstream& inp) { ...
while(inP) { //creeaza un obiect vid Figura* pfig = FabricaDeFig::Instanta().
creeazaFig(figId); ...
} }
D. Lucanu POO – Proiectarea de clase 49
Fabrica de obiecte :: inregistrare (rafinat) namespace { Figura* creeazaSegm() { ....
} ...
// inregistreaza const bool registeredSEGM = FabricaDeFig::Instanta() .inregistreazaFig( SEGMID, creeazaSegm ); }
D. Lucanu POO – Proiectarea de clase 50
Codul din GoF
D. Lucanu POO – Proiectarea de clase 51