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 DelegatMap; DelegatMap delegati_; };

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