02a3creational Design Patterns
02a3creational Design Patterns
Patterns
Creational DP:
Abstracts the instantiation process.
Help make a system independent of how objects are
created, composed, represented.
Two types of Creational Patterns
1. Class creational patterns
Use inheritance to vary the class that is instantiated
2. Object creational patterns
Delegates instantiation to another object
Creational Design
Patterns
Two recurring themes in these patterns
1. They encapsulate knowledge about which
concrete classes the system use.
2. They hide how instances of these classes are
created and put together.
The system only knows the interfaces.
Creational DP-s let you configure the system
with “product” objects
Configuration can be static (compile-time) or
dynamic (run-time).
Example: To build a
Maze
Maze Example –
MapSite Abstract class
enum Direction
{ North, South, East, West } ;
class MapSite {
public:
void Enter() = 0 ;
} ;
Maze Example –
Room
class Room: public MapSite
{
public:
Room(int roomNo) ;
{ roomNumber = number ; }
MapSite* GetSide(Direction d) const
{ return sides[d] ; }
void SetSide(Direction d, MapSite* m)
{ sides[d] = m ; }
virtual void Enter()
{ /* … do something … */ }
private:
MapSite* sides[4] ;
int roomNumber ;
} ;
Maze Example –
Wall and Door
class Wall: public MapSite {
public:
Wall() ;
virtual void Enter() ;
} ;
class Door: public MapSite {
public:
Door(Room*=0, Room*=0) ;
virtual void Enter() ;
Room* OtherSideFrom(Room*);
private:
Room* room1 ;
Room* room2 ;
bool isOpen;
} ;
Maze Example –
Collection of Rooms
class Maze {
public:
Maze() ;
void addRoom(Room r) ;
Room* RoomNo(int) const;
private:
// …
};
Maze Example –
Creation of Maze
class MazeGame
{
public:
Maze* CreateMaze() {
Maze* maze = new Maze() ;
Room* room1 = new Room(1) ;
Room* room2 = new Room(2) ; Room 1 Room 2
Door* door = new Door(room1,room2) ;
maze->AddRoom(room1) ;
maze->AddRoom(room2) ;
room1->SetSide(North, new Wall()) ;
room1->SetSide(East , door) ;
room1->SetSide(South, new Wall()) ;
room1->SetSide(West , new Wall()) ;
room2->SetSide(North, new Wall()) ;
room2->SetSide(East , new Wall()) ;
room2->SetSide(South, new Wall()) ;
room2->SetSide(West , door) ;
return maze ;
}
}
Creational Patterns
Factory Method
Create-Maze calls virtual functions to create
components
Abstract Factory
Create-Maze is passed an object to use to create
components
Prototype
Create-Maze is parameterized by various prototypes
Builder
Create-Maze is passed an object that can create
entire Maze
Singleton
Can ensure that there is only one maze per game.
Factory Method
Intent: Define an interface for creating an object,
but let subclasses decide which class to
instantiate.
Motivation:
Example: Framework of Abstract classes
○ Abstract classes: Document, Application
Application has Open, New, etc. to create new documents
Application cannot know which concrete document to
instantiate
○ Concrete classes: DrawingDocument,
DrawingApplication
Factory Method encapsulates the knowledge of
which Document subclass to create and move this
knowledge out of the framework.
Factory Method –
Motivation
Use Singleton
There must be exactly one instance of a class, and it
must be accessible to clients from a well known
access point.
When this instance should be extensible by sub-
classing
Singleton
Singleton
Define an Instance operation to access its unique
instance. It must be a static method.
Must create its own unique instance.
Singleton –
Benefits
1. Controlled access to sole instance
2. Reduced namespace
3. May be sub-classed to refine
operations
4. Can Permit a variable number of
instances
5. More flexible than static methods
Singleton –
Implementation
Ensure a unique instance
class Singleton {
private: static Singleton* inst = 0 ;
protected: Singleton() { }
public: static Singleton* getInstance() {
if (inst==0) inst = new Singleton() ;
return inst ;
}
} ;
Subclassing the singleton class
How to install the unique instance?
○ To determine it in getInstance() method
○ To rewrite getInstance() in the subclass
○ To keep registry of Singletons
Singleton –
Maze Factory
class MazeFactory {
protected: MazeFactory() { }
private: static MazeFactory* inst = null ;
public: static MazeFactory* getInst()
{ if (inst==null) inst = new MazeFactory() ;
return inst ; }
Maze* makeMaze()
{ return new Maze() ; }
Room* makeRoom(int n)
{ return new Room(n) ; }
Wall* makeWall()
{ return new Wall() ; }
Door* makeDoor(Room r1, Room r2)
{ return new Door(r1,r2) ; }
} ;
Singleton –
Maze Example
class MazeGame
{
public:
Maze* createMaze() {
Maze maze* = MazeFactory.getInst()->MakeMaze() ;
Room room1* = MazeFactory.getInst()-
>MakeRoom(1) ;
Room room2* = MazeFactory.getInst()-
>MakeRoom(2) ;
Door door* =
MazeFactory.getInst()->MakeDoor(room1,room2) ;
maze->AddRoom(room1) ;
maze->AddRoom(room2) ;
………
return maze ;
}
}
Singleton –
Alternative Maze Factory
MazeFactory* MazeFactory::getInst()
{ if (inst==0) {
const char* style = getenv("MAZESTYLE")
;
if (strcmp(style,"BOMBED“))
inst = new BombedMazeFactory() ;
else if (strcmp(style,"BOMBED“))
inst = new
EnchantedMazeFactory() ;
else
inst = new MazeFactory() ;
}
return inst ; }
Template Singleton
Class
// in .h
template <class T>
class Singleton : public T
{
public:
static Singleton* GetInstance()
{
if (! ptrSingObject)
ptrSingObject = new Singleton ;
return ptrSingObject ;
}
~Singleton() { delete ptrSingObject ; }
private:
Singleton() { } ;
static Singleton* ptrSingObject ;
};
// In .cpp
template <class T>
Singleton<T>* Singleton<T>::ptrSingObject = NULL ;
// usage
class CMyClass {
void myfunc() ;
} ;
// In the program to use
Singleton<CMyClass>::GetInstance()->myfunc() ;
Singleton in multi-threaded
environment
Singletons used in multi-theaded systems are
susceptible to a “race” condition during execution of
the instance() function which can allow creation of
multiple instances (oh oh…multiple singletons?) and
cause a memory leak.
Threads can try to update the same data structure at
the same time.
The result can be partly what one thread wrote and
partly what the other thread wrote.
This garbles the data structure, typically causing the
next thread that tries to use it to
crash
Solution
In the instance() function, if self is zero,
then aquire a lock (semaphore, mutex,
etc.). Next, double check to insure that
self is still zero If self is still zero, then
create an instance of the Singleton and
assign it to self. Release the lock and
return.
Example code
class Singleton {
public:
static Singleton * instance();
private:
static Singleton * self;
static SEMAPHORE key
}
Singleton * Singleton::instance() {
if (self == 0) {
if ( lock(key) >= 0 ) {
if (self == 0 ) //double-check!
self = new Singleton;
unlock (key);
}}}
Client (GraphicTool)
Creates a new object by asking a prototype to clone
itself.
Prototype –
Consequences
It hides concrete product classes from the
client
Let a client work with application-specific
classes without modification
1. Adding and removing products at run-time.
2. Specifying new objects by varying values.
3. Specifying new objects by varying structure.
4. Reduced subclassing.
5. Configuring an application with classes
dynamically.
Adding Clone() to each product may be
difficult.
Prototype –
Implementation Issues
1. Using a prototype manager.
To keep a registry of possible
prototypes.
2. Implementing the Clone operation.
Shallow Copy versus Deep Copy.
Serialization (Save-Load) can be used
for cloning.
3. Initializing clones.
Set operations can solve this problem.
Prototype – Sample
class MazePrototypeFactory : public MazeFactory {
private:
Maze* pMaze; Wall* pWall; Room* pRoom; Door* pDoor;
public:
MazePrototypeFactory ( Maze* m, Wall* w, Room* r, Door* d)
: pMaze(m), pWall(w), pRoom(r), pDoor(d)
{ }
virtual Maze* MakeMaze() const
{ return pMaze->Clone() ; }
virtual Room* MakeRoom(int r) const
{ Room* p = pRoom->Clone() ;
p->initialize(r) ;
return p; }
virtual Wall* MakeWall() const
{ return pWall->Clone(); }
virtual Door* MakeDoor(Room* r1, Room* r2) const
{ Door* door = pDoor->Clone();
door->initialize(r1,r2);
return door; }
};
Prototype – Sample
MazeGame game;
MazePrototypeFactory simpleMazeFactory(new
Maze, new Wall, new Room, new Door);
MazePrototypeFactory bombedMazeFactory(new
Maze, new BombedWall, new RoomWithABomb,
new Door);
Maze* maze1 =
game.CreateMaze(simpleMazeFactory);
Maze* maze2 =
game.CreateMaze(bombedMazeFactory);
Prototype – Sample
class Door: public MapSite {
private:
Room* room1;
Room* room2;
public:
Door(): room1(0), room2(0)
{ }
Door(const Door& d): room1(d.room1), room2(d.room2)
{ }
virtual void Initialize(Room* r1, Room* r2)
{ room1 = r1 ;
room2 = r2 ; }
virtual Door* Clone() const {
return new Door(*this) ;
}
} ;
Prototype – Sample Code
class BombedWall: public Wall {
private:
bool bomb;
public:
BombedWall() : bomb(true)
{ }
BombedWall(bool b) : bomb(b)
{ }
BombedWall(const BombedWall& other)
{ bomb = other.bomb; }
virtual Wall* Clone() const
{ return new BombedWall(*this) ; }
bool HasBomb()
{ return bomb; }
} ;
Creational Design Patterns
–
Discussion
Two ways two parameterize a system by the
classes of objects it creates.
1. To subclass the class that creates objects
- Factory Method
Main drawback is that it requires a new subclass
just to change the class of the product.
2. By object composition
- Abstract Factory: Factory Object producing
objects of several classes
- Builder: Factory Object building a complex product
- Prototype: Factory Object building a product by
copying a prototype object.