Post on 14-Dec-2015
Creational Patterns,Creational Patterns,Abstract Factory,Abstract Factory,BuilderBuilder
Billy BennettJune 11, 2009
Creational PatternsCreational Patterns
Two Recurring Themes:
1.Encapsulate knowledge about which concrete classes the system uses.
2.Hide how these instances are created and put together.
The Maze ExampleThe Maze Example
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);
return aMaze;
}
Uh-ohUh-ohThis gets complicated.It’s inflexibly coded.It’s difficult to reuse.Only makes 2 rooms.Creational patterns let us make
this design more flexible and thus more reusable.
Potential PatternsPotential PatternsVirtual functions instead of
constructors for◦Rooms◦Doors◦Walls
Make a subclass of MazeGame to redefine these functions
Factory Method
Potential PatternsPotential PatternsCreateMaze is passed an object
as a parameter, which is used to change the classes used to make◦Rooms◦Doors◦Walls
Abstract Factory
Potential PatternsPotential PatternsCreateMaze is passed an object
that can create a maze all by itself, then you can use inheritance to change parts of the maze:◦Rooms◦Doors◦Walls
Builder
Potential PatternsPotential PatternsCreateMaze is parameterized by
various prototypical:◦Rooms◦Doors◦Walls
You can replace these prototypical objects with different ones to change the maze
Prototype
Abstract FactoryAbstract Factory
Abstract FactoryAbstract FactoryIntent
◦Provide an interface for creating families of related or dependent objects without specifying their concrete classes
A.K.A.◦“Kit”
ApplicabilityApplicabilityA system should be independent of
how its products are created, composed, or represented
A system should be configured with one of multiple families of products
A family of related product objects is designed to be used together
You want to provide a class library of objects, but reveal only their interfaces
ParticipantsParticipantsClientAbstractFactoryConcreteFactoryAbstractProductConcreteProduct
ConsequencesConsequences
1. Concrete class isolation (Good)• Client does not interact with the
implementation classes• Client only manipulates instances
through the abstract interfaces
ConsequencesConsequences
2. Product families easily exchanged (Good)• Only have to change the concrete
factory• Can be done at run time
ConsequencesConsequences
3. Products are more consistent (Good)• Helps the products in each product
family consistently be applied together (assuming they work well together)
• Only one family at a time
ConsequencesConsequences
4. Difficult to support new kinds of products (Bad)• Extending existing abstract
factories to make new products is difficult and time consuming
• The family of products available is fixed by Abstract Factory interface
Implementation IssuesImplementation IssuesConcrete Factories make excellent
Singletons (not necessarily one factory, but one per product family)
Factory Methods within Concrete Factories for each product, or Prototypes if many product families
Move implementation one step closer to Client by adding a parameter specifying product type (less safe, more extensible)
Class MazeFactory {
Public:
MazeFactory();
virtual Maze* MakeMaze () const
{return new Maze;}
virtual Wall* MakeWall () const
{return new Wall;}
virtual Room* MakeRoom (int n) const
{return new Room(n);}
virtual Door* MakeDoor (Room* r1, Room* r2)
{return new Door(r1, r2);}
};
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);
return aMaze;
}
Maze* MazeGame::CreateMaze (MazeFactory& factory)
{
Maze* aMaze = factory.MakeMaze();
Room* r1 = factory.MakeRoom (1);
Room* r2 = factory.MakeRoom (2);
Door* theDoor = factory.MakeDoor (r1, r2);
aMaze->AddRoom(r1);
aMaze->AddRoom(r2);
r1->SetSide(North, factory.MakeWall);
r1->SetSide(East, theDoor);
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, theDoor);
return aMaze;
}
Related PatternsRelated PatternsFactory MethodPrototypeSingleton
BuilderBuilderIntent
◦Separate the construction of a complex object from its representation so that the same construction process can create different representations
ApplicabilityApplicabilityThe algorithm for creating a
complex object should be independent of the parts that make up the object and how they’re assembled
The construction process must allow for different representations for that object that’s constructed
ParticipantsParticipantsDirectorBuilderConcrete BuilderProduct
ConsequencesConsequences
1. Varying a product’s internal representation (Good)• The Director doesn’t see the
product’s construction, only the Builder does
ConsequencesConsequences
2. Isolates code for construction and representation (Good)• The Client only retrieves the
product• The Client doesn’t know anything
about the internal construction of the product
• Question: Multiple directors?
ConsequencesConsequences
3. Step by step construction (Good?)• The Client only retrieves the
product• The Client doesn’t know anything
about the internal construction of the product
• Question: Multiple directors?
Implementation IssuesImplementation IssuesBuilder needs a very general
interfaceBuilder(s) may need access to a
variety of components – parse trees are one solution
No abstract product class (products are too different, commonly)
Class MazeBuilder {
public:
virtual void BuildMaze() {. . .}
virtual void BuildRoom (int room) {. . .}
virtual void BuildDoor (int roomFrom, int roomTo) {. . .}
virtual Maze* GetMaze() {return Maze to client}
Protected:
MazeBuilder();
};
Maze* MazeGame::CreateMaze (MazeBuilder& builder)
{
builder.BuildMaze();
builder.BuildRoom(1);
builder.BuildRoom(2);
builder.BuildDoor(1,2);
return builder.GetMaze();
}
Other thoughtsOther thoughtsThe “abstract interface” theme
among patternsOOP bias?Why do some patterns seem so
similar – is there a standard for defining novelty among patterns?