Sunteți pe pagina 1din 56

Şabloane Creaţionale

Curs 5

1
Introducere în Şabloanele Creaţionale

 Abstractizează procesul de instanţiere


 Sistemul devine independent de cum sunt create, compuse,
reprezentate obiectele sale

 Şablon creaţional tip clasă


 foloseşte moştenirea pentru a varia clasa instanţiată
 Factory Method

 Şablon creaţional tip obiect


 delegă instanţierea către alt obiect
 Abstract Factory, Prototype, Singleton, Builder

2
Diagrama de clase pentru Labirint
4 MapSite

enter()=0;

1
rooms Room Wall Door
Maze
1 * enter()
enter() enter()
setSide()
getSide() isOpen

roomNumber

3
Clase abstracte comune tuturor Componentelor
Labirintului
enum Direction {North, South, East, West};

class MapSite {
public:
virtual void enter() = 0;
};

 Înţelesul lui enter() depinde de unde se intră.


 cameră  modificarea locaţiei
 uşă  dacă uşa este deschisă se poate intra; altfel...

4
Componentele labirintului – Perete & Uşă & Cameră
class Wall : public MapSite {
public:
Wall();
class Room : public MapSite {
virtual void enter();
public:
};
Room(int roomNo);
MapSite* getSide(Direction) const;
void setSide(Direction, MapSite*);
class Door : public MapSite {
void enter(); public:
private: Door(Room* = 0, Room* = 0);
MapSite* sides[4]; virtual void enter();
int roomNumber; Room* otherSideFrom(Room*);
} private:
Room* room1;
Room* room2;
bool isOpen;
};

5
Componentele labirintului – Maze

Un labirint este o colecţie


class Maze {
public:
de camere. “Maze” poate
void addRoom(Room*); găsi o cameră anume
Room * roomNo(int) const; fiind dat numărul camerei.
private:
}; roomNo() face o verificare
folosind căutare lineară
sau funcţie hash sau un
tablou.

6
Desfăşurarea jocului!
4
MapSite
MazeGame enter()=0;

1
1
rooms Room Wall Door
Maze 1 1..n enter()
enter() enter()
setSide()
getSide() isOpen
1..n
roomNumber
Player

7
Crearea Labirintului
Maze* MazeGame::createMaze() {
Maze* aMaze = new Maze;
Room* r1 = new Room(1);
Room* r2 = new Room(2);
Door* theDoor = new Door(r1, r2);
aMaze->addRoom(r1);
aMaze->addRoom(r2);

r1->setSide(North, new Wall); r1->setSide(East, theDoor);


r1->setSide(South, new Wall); r1->setSide(West, new Wall);

r2->setSide(North, new Wall); r2->setSide(East, new Wall);


r2->setSide(South, new Wall); r2->setSide(West, theDoor);
}
 Problemă: inflexibilitatea
 codificarea aspectului labirintului
 Şabloanele pot face crearea jocului mai flexibilă... nu mai scurtă!

8
Dorim Flexibilitate în Crearea Labirintului

 Să putem varia tipurile de labirinturi


 Camere cu bombe
 Pereţi bombardaţi
 Camere speciale
 Trebuie o abilitate aparte să intri pe uşă!

9
Ideea 1:
Derivăm MazeGame, suprascriem createMaze
Maze* BombedMazeGame::createMaze() {
Maze* aMaze = new Maze;
Room* r1 = new RoomWithABomb(1);
Room* r2 = new RoomWithABomb(2);
Door* theDoor = new Door(r1, r2);
aMaze->addRoom(r1);
aMaze->addRoom(r2);

r1->setSide(North, new BombedWall);


r1->setSide(East, theDoor);
r1->setSide(South, new BombedWall);
r1->setSide(West, new BombedWall);
// etc...etc...
}

 Mult cod duplicat... :((

10
Ideea 2:
Folosim Metoda Factory
4 MapSite

MazeGame enter()=0;

makeWall()
createMaze()
1
Room Wall Door
enter()
enter() enter()
BombedMazeGame setSide()
getSide() isOpen
makeWall()
roomNumber
BombedWall
return new BombedWall
enter()

Vezi slide nr. 24 11


Metoda Factory

12
Aspecte de Bază
 Scop
 Defineşte o interfaţă pentru crearea unui obiect, dar lasă subclasele să
decidă ce clasă să instanţieze.
 Metoda Factory permite unei clase să defere instanţierea subclaselor

 Cunoscut şi ca
 Constructor Virtual

 Aplicabilitate
 O clasă nu poate anticipa clasa obiectelor pe care trebuie să le creeze
 O clasă doreşte ca subclasele sale să specifice obiectele pe care le
creează
 Clasele delegă responsibilitatea uneia sau mai multor subclase ajutătoare

13
Structură

14
Participanţi & Colaborări
 Product
 defineşte interfaţa obiectelor ce vor fi create de FM
 Produs Concret implementează interfaţa

 Creator
 declară FM, care returnează un produs de tip Product.
 poate defini o implementare implicită a FM
 poate apela FM pentru crearea unui produs

 ConcreteCreator
 suprascrie FM pentru a furniza o instanţă a ConcreteProduct

 Creator se bazează pe subclasele sale pentru definirea metodei


factory astfel încât să returneze instanţa potrivită a clasei
ConcreteProduct
15
Consecinţe

 Elimină legarea între clasele specifice aplicaţiei din cod


 codul de creare foloseşte doar interfaţa clasei Product

 Facilitează derivarea
 subclasele pot astfel modifica produsul creat

 Poate conecta ierarhii paralele de clase


 lasă clienţii să apeleze FM
 Slide-ul următor...

 Clienţii ar trebui să deriveze din Creator doar pentru a crea un


anumit obiect ConcreteProduct.

16
Conectarea Ierarhiilor Paralele de Clase

 Localizează informaţia despre clasele ce aparţin împreună

17
Detalii de Implementare
 Varietăţi ale Metodelor Factory
 Clasa Creator e abstractă
 nu implementează FM declarată
 necesită subclase
 folosită pentru instanţierea claselor neprevăzute
 Clasa Creator e concretă
 creează o implementare implicită
 FM folosite pentru flexibilitate
 Creează obiecte într-o operaţie separată astfel încât subclasele o
pot suprascrie
 Parametrizarea Metodelor Factory
 O variaţie a şablonului lasă metodele factory să creeze mai
multe tipuri de produse
 un parametru identifică tipul de Product de creat
 Toate obiectele create au aceeaşi interfaţă cu Product

18
Parametrizarea Metodei Factory
class Creator {
public:
virtual Product * create(productId);
};

Product* Creator::create(ProductId id) {


if (id == MINE) return new MyProduct;
if (id == YOURS) return new YourProduct;
}

Product * MyCreator::create(ProductId id) {


if (id == MINE) return new YourProduct;
if (id == YOURS) return new MyProduct;
if (id == THEIRS) return TheirProduct;
return Creator::create(id); // apelată dacă eşuează
celelalte
}
 selectiv extinde sau modifică produsele care se creează
19
Java: forName şi Metodele Factory

class Creator {
private String productType;

public Creator( String theProduct ) {


productType = theProduct;
}

public FactoryMethod( ) {
Class productClass = Class.forName(productType);
return (Product) productClass.newInstance();
}

Product theBest = new Creator( "ProductA" ).FactoryMethod();

20
Metoda Factory poate fi parametrizată:
class ProductA extends Product {
import java.util.*; public void doSomething()
{System.out.println("ProductA");
class AbstractFactory { }}
public Product make(String c) {
try { class ProductB extends Product {
Class prod = Class.forName(c); public void doSomething()
{System.out.println("ProductB");
return }}
(Product)prod.newInstance();}
class Main {
catch(Exception e) { public static void main(String[] args)
System.out.println("Error"); {AbstractFactory af = new
System.exit(1); AbstractFactory();
return null; } }} af.make(args[0]).doSomething();
}}
abstract class Product {
abstract public void
doSomething();
}
21
C++: Template-uri pentru evitarea derivării

template <class ProductType>


class Creator
{
public:
virtual Product* FactoryMethod();
}

template <class ProductType>


Product* Creator::FactoryMethod( ) {
return new ProductType();
}

// ....
Creator<ConcreteProduct> theBest;

22
Soluţia Problemei Labirintului (reluare)...

4 MapSite

MazeGame enter()=0;

makeWall()
createMaze()
1
Room Wall Door
enter()
enter() enter()
BombedMazeGame setSide()
getSide() isOpen
makeWall()
roomNumber
BombedWall
return new BombedWall
enter()

23
Clasa MazeGame şi metoda CreateMaze()
class MazeGame {public: Maze* MazeGame::CreateMaze ()
Maze* createMaze(); {Maze* aMaze = makeMaze();
// metode factory: Room* r1 = makeRoom(1);
virtual Maze* makeMaze() const Room* r2 = makeRoom(2);
Door* theDoor =makeDoor(r1, r2);
{ return new Maze; }
aMaze->addRoom(r1);
virtual Room* makeRoom(int n) const aMaze->addRoom(r2);
{ return new Room(n); } r1->SetSide(North, makeWall());
r1->SetSide(East, theDoor);
virtual Wall* makeWall() const r1->SetSide(South, makeWall());
{ return new Wall; }
r1->SetSide(West, makeWall());
r2->SetSide(North, makeWall());
virtual Door* makeDoor(Room* r1,
Room* r2) const
r2->SetSide(East, makeWall());
r2->SetSide(South, makeWall());
{ return new Door(r1, r2); }};
r2->SetSide(West, theDoor);
return aMaze; }

24
Ideea 3:
Metodă Factory în Product

 Faceţi produsul responsabil de propria creare


 e.g. lăsaţi clasa Door să ştie cum să-şi construiască instanţele, în
locul clasei MazeGame

 Clientul produsului necesită o referinţă la "creator"


 specificată în constructor

25
Cod exemplu- este acesta un Prototype?

class Room : public MapSite { class MazeGame {


public: protected:
virtual Room* makeRoom(int Room* roomMaker;
no) // ...
{ return new Room(no); } public:
// ...}; MazeGame(Room* rfactory) {
roomMaker = rfactory;}
class RoomWithBomb : public
Room { public Maze* CreateMaze() {
public: Maze aMaze = new Maze();
Room* makeRoom(int no) Room r1 = roomMaker->makeRoom(1);
{ return new RoomWithBomb(); } // ...
// ... };
};
// ...

26
Şablonul Prototype

27
Introducere
 Scop
Specificaţi tipul obiectelor care se creează folosind o
instanţă prototipică
Creaţi obiecte noi copiind acest prototip

 Aplicabilitate
când un sistem trebuie să fie independent de cum
sunt create, compuse şi reprezentate produsele sale şi
când clasele de instanţiat sunt specificate la execuţie
evitaţi construirea unei ierarhii de clase-factory
paralelă cu ierarhia claselor de produse

28
Structură

29
Participanţi & Colaborări
 Prototype
 declară o interfaţă pentru a se clona.

 ConcretePrototype
 implementează o operaţie pentru a se clona.

 Client
 creează un obiect nou cerând prototipului să se cloneze.

 Un client cere unui prototip să se cloneze.

 Clasa client trebuie să se iniţializeze în constructor


 cu prototipul concret adecvat.

30
Consecinţe
 Adăugarea şi ştergerea produselor la execuţie
 Mai puţină derivare
 evită ierarhia paralelă pentru creatori
 Specificarea de obiecte noi prin varierea valorilor
prototipurilor
 clientul se comportă diferit prin delegarea către prototip
 Specificarea de obiecte noi prin varierea structurii
prototipurilor
 produse compuse

 Fiecare subclasă a Prototype trebuie să implementeze clone


 dificil când clasele există deja sau
 obiectele interne nu permit copierea sau au referinţe circulare

31
Detalii de Implementare

 Folosirea unui gestionar de Prototipuri


 numărul de prototipuri nu e fixat
 ţineţi un registru  manager de prototipuri
 Clienţii vor cunoaşte doar managerul (nu prototipul)
 stocare asociativă

 Implementarea operaţiei clone


 copie de suprafaţă vs. copie de profunzime

 Iniţializarea clonelor
 eterogenitatea metodelor de iniţializare
 scrieţi o metodă Initialize

32
Copie de Suprafaţă vs. Copie de Profunzime

Original

Copie de suprafaţă

33
Copie de Suprafaţă vs. Copie de Profunzime (2)

Original

Copie de Profunzime

34
Clonarea în C++ – Constructorii de Copiere
class Door {
public:
Door();
Door( const Door&);
virtual Door* clone() const;
virtual void Initialize( Room*, Room* );
private:
Room* room1; Room* room2;
};

//constructor de copiere
Door::Door ( const Door& other ) {
room1 = other.room1; room2 = other.room2;
}

Door* Door::clone() {
return new Door( *this );
}

35
Clonarea în Java – Object clone()

protected Object clone() throws CloneNotSupportedException

 Creează o clonă a obiectului.


 alocă o nouă instanţă şi,
 plasează o clonă bit cu bit a obiectului curent în noul obiect.

class Door implements Cloneable {


public void Initialize( Room a, Room b) {
room1 = a; room2 = b;
}

public Object clone() throws CloneNotSupportedException {


return super.clone();
}
Room room1, room2;
}

36
Rezolvarea Problemei Labirintului
class MazePrototypeFactory { Wall* MazePrototypeFactory::MakeWall () const
public: {return _prototypeWall->Clone();}
MazePrototypeFactory(Maze*, Wall*,
Room*, Door*); Door* MazePrototypeFactory::MakeDoor (
Room* r1, Room *r2) const {
virtual Maze* MakeMaze() const; Door* door = _prototypeDoor->Clone();
virtual Room* MakeRoom(int) const; door->Initialize(r1, r2);
virtual Wall* MakeWall() const; return door;}
virtual Door* MakeDoor(Room*, Room*)
const; Crearea unui labirint pentru un joc…
private: MazePrototypeFactory simpleMazeFactory
Maze* _prototypeMaze; (new Maze, new Wall, new Room, new Door);
Room* _prototypeRoom; MazeGame game;
Wall* _prototypeWall; Maze* maze =
Door* _prototypeDoor;}; game.CreateMaze(simpleMazeFactory);

MazePrototypeFactory::MazePrototypeFactory (
Maze* m, Wall* w, Room* r, Door* d)
{ _prototypeMaze = m;
_prototypeWall = w;
_prototypeRoom = r;
_prototypeDoor = d;}

37
Abstract Factory

38
Exemplu Introductiv

39
Aspecte de bază
 Scop
 Oferă o interfaţă pentru crearea de familii de obiecte înrudite
sau dependente fără specificarea claselor lor concrete

 Aplicabilitate
 Sistemul trebuie să fie independent de cum sunt create, compuse
şi reprezentate produsele sale
 Sistemul trebuie configurat de una din mai multe familii de
produse
 Trebuie forţat ca o familie de obiecte produs să fie folosite
împreună

40
Structură

41
Participanţi & Colaborări
 Abstract Factory
 declară o interfaţă pentru operaţii pentru crearea de produse
abstracte
 ConcreteFactory
 implementează operaţiile de creare a produselor
 AbstractProduct
 declară o interfaţă pentru un tip de obiecte produs
 ConcreteProduct
 implementează o interfaţă pentru un tip de obiecte produs
 Client
 foloseşte doar interfeţe decl. de AbstractFactory şi
AbstractProduct

 O singură instanţă a ConcreteFactory creată.


 creează produse cu o implementare particulară
42
Consecinţe
 Izolarea claselor concrete
 apar în ConcreteFactories nu în codul clientului

 Facilitează schimbul de familii de produse


 un ConcreteFactory apare într-un singur loc
 uşor de modificat

 Promovează consistenţa între produse


 toate produsele unei familii se modifică împreună, în acelaşi
timp

 Este dificilă susţinerea de tipuri noi de produse


 necesită o modificare în interfaţa AbstractFactory
 ... şi a tuturor subclaselor sale, în consecinţă

43
Detalii de Implementare
 Fabrici ca Singleton-uri
 pentru a asigura că doar un singur ConcreteFactory este creat
pentru fiecare familie de produse

 Crearea Produselor
 colecţie de Metode Factory
 poate fi implementat şi folosind Prototype
 definiţi o instanţă prototipică pentru fiecare produs din
ConcreteFactory

 Definirea de “Fabrici” Extensibile


 o singură metodă factory cu parametri
 mai flexibil, mai nesigur!

44
Crearea Produselor folosind propriile metode factory

abstract class WidgetFactory {


public Window createWindow();
public Menu createMenu();
public Button createButton();
}

class MacWidgetFactory extends WidgetFactory {


public Window createWindow()
{ return new MacWidow() }
public Menu createMenu()
{ return new MacMenu() }
public Button createButton()
{ return new MacButton() }
}

45
Crearea Produselor folosind metodele factory ale produselor

 subclasa doar furnizează produsele concrete în constructor


 evită reimplementarea FM în subclase

abstract class WidgetFactory {


private Window windowFactory;
private Menu menuFactory;
private Button buttonFactory;
public Window createWindow()
{ return windowFactory.createWindow() }
public Menu createMenu();
{ return menuFactory.createWindow() }
public Button createButton()
{ return buttonFactory.createWindow() }
}

class MacWidgetFactory extends WidgetFactory {


public MacWidgetFactory() {
windowFactory = new MacWindow();
menuFactory = new MacMenu();
buttonFactory = new MacButton();
}
}

46
Rezolvarea Problemei Labirintului....
class MazeFactory { Maze* MazeGame::CreateMaze (MazeFactory&
public: factory) {
MazeFactory(); Maze* aMaze = factory.MakeMaze();
Room* r1 = factory.MakeRoom(1);
virtual Maze* MakeMaze() const Room* r2 = factory.MakeRoom(2);
{ return new Maze; } Door* aDoor = factory.MakeDoor(r1, r2);
virtual Wall* MakeWall() const
{ return new Wall; } aMaze->AddRoom(r1);
virtual Room* MakeRoom(int n) const aMaze->AddRoom(r2);
{ return new Room(n); }
virtual Door* MakeDoor(Room* r1, Room* r2) r1->SetSide(North, factory.MakeWall());
const r1->SetSide(East, aDoor);
{ return new Door(r1, r2); } r1->SetSide(South, factory.MakeWall());
}; r1->SetSide(West, factory.MakeWall());

r2->SetSide(North, factory.MakeWall());
r2->SetSide(East, factory.MakeWall());
r2->SetSide(South, factory.MakeWall());
r2->SetSide(West, aDoor);

return aMaze;
}

47
Singleton

48
Introducere
 Scop
 Asigură ca o clasă să aibă doar o singură instanţă şi furnizează un
punct de acces global la ea

 Aplicabilitate
 dorim exact o instanţă a unei clase
 accesibilitate pentru clienţi dintr-un singur punct
 dorim ca instanţa să fie extensibilă
 poate permite deasemenea şi un set numărabil de instanţe
 optimizare faţă de vizibilitatea globală
 mai bine decât o clasă statică:
 nu se poate răzgândi
 metode niciodată virtuale

49
Structura Şablonului

Plasaţi constructorul în secţiunea de date private/protected

50
Participanţi şi Colaborări
 Singleton
 defineşte o metodă Instance care devine singura “poartă" prin
care clienţii pot accesa instanţa sa unică.
 Instance este o metodă a clasei (funcţie membră statică în C++)
 Poate fi responsabil cu crearea propriei sale instanţe unice

 Clienţii accesează instanţele Singleton doar prin metoda


Instance

51
Consecinţe

 Acces controlat la instanţa unică

 Permite rafinarea operaţiilor şi reprezentării

 Permite un număr variabil (dar precis) de instanţe

 Reducerea vizibilităţii globale

52
Implementare: Asigurarea Instanţei Unice

53
Implementare: Derivarea Singleton

54
Crearea unei singure MazeFactory

55
Şi dacă există subclase ale MazeFactory?

56