900 likes | 1.18k Views
Chapter 3. Creational Patterns. Design Patterns Elements of Reusable Object-Oriented Software. Creational Patterns. Creational design patterns abstract the instantiation process A class creational pattern uses inheritance to vary the class that’s instantiated
E N D
Chapter 3 Creational Patterns Design PatternsElements of Reusable Object-Oriented Software
Creational Patterns • Creational design patterns abstract the instantiation process • A class creational pattern uses inheritance to vary the class that’s instantiated • An object creational pattern will delegate instantiation to another object
Example: A Game of Maze MapSite Enter() sides Room Wall Door Enter() SetSide() GetSide() Enter() Enter() Maze isOpen rooms AddRoom() RoomNo() roomNumber
Class - Mapsite class MapSite { public: virtual void Enter() = 0; }; enum Direction {North, South, East, West};
Class - Room class Room : public MapSite { public: Room(int roomNo); MapSite* GetSide(Direction) const; void SetSide(Direction, MapSite*); virtual void Enter(); private: MapSite* _sides[4]; int _roomNumber; };
Class - Wall class Wall : public MapSite { public: Wall(); virtual void Enter(); };
Class - Door class Door : public MapSite { public: Door(Room* = 0, Room* = 0); virtual void Enter(); Room* OtherSideFrom(Room*); private: Room* _room1; Room* _room2; bool _isOpen; };
Class - Maze class Maze { public: Maze(); void AddRoom(Room*); Room* RoomNo(int) const; private: // … };
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; } Class – MazeGame
Making the design more flexible • CreateMaze calls virtual functions instead of constructor calls (Factory Method) • CreateMaze is passed an object as a parameter to use to create rooms, walls and doors (Abstract Factory) • CreateMaze is passed an object that can create a new maze in its entirety using operations for adding rooms, doors, and walls to the maze it builds (Builder) • CreateMaze is parameterized by various prototypical room, door, and wall objects, which it then copies and adds to the maze (Prototype) • The remaining creational pattern, Singleton, can ensure there’s only one maze per game and that all game objects have ready access to it – without resorting to global variables or functions
Abstract Factory Object Creational
Abstract Factory • Intent • Provide an interface for creating families of related or dependent objects without specifying their concrete classes • Also Known As • Kit
Abstract Factory - Motivation WidgetFactory Client CreateScrollBar()CreateWindow() Window MotifWidgetFactory PMWindow MotifWindow CreateScrollBar()CreateWindow() ScrollBar PMWidgetFactory CreateScrollBar()CreateWindow() PMScrollBar MotifScrollBar
Abstract Factory - Structure AbstractFactory Client CreateProductA()CreateProductB() AbstractProductA ConcreteFactory1 ProductA2 ProductA1 CreateProductA()CreateProductB() AbstractProductB ConcreteFactory2 CreateProductA()CreateProductB() ProductB2 ProductB1
Abstract Factory - Consequences • Benefits • It isolates concrete classes • It makes exchanging product families easy • It promotes consistency among products • Liabilities • Supporting new kinds of products is difficult
Abstract Factory - Implementation • Factories as singletons • Creating the products • Define a factory method for each product • Using the Prototype pattern • Defining extensible factories • Add a parameter (e.g. a class identifier or a class name) to operations that create objects • Problem: Needs downcast, which may not be safe
Abstract Factory – Class - MazeFactory 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) const { return new Door(r1, r2); } };
Maze* MazeGame::CreateMaze( MazeFactory& factory ) { Maze* aMaze = factory.MakeMaze(); Room* r1 = factory.MakeRoom(1); Room* r2 = factory.MakeRoom(2); Door* aDoor = factory.MakeDoor(r1, r2) aMaze->AddRoom(r1); aMaze->AddRoom(r2); r1->SetSide(North, factory.MakeWall()); r1->SetSide(East, aDoor); r1->SetSide(South, factory.MakeWall()); r1->SetSide(West, factory.MakeWall()); Abstract Factory – MazeGame r2->SetSide(North, factory.MakeWall()); r2->SetSide(East, factory.MakeWall()); r2->SetSide(South, factory.MakeWall()); r2->SetSide(West, aDoor); return aMaze; }
Abstract Factory – Class - EnchantedMazeFactory class EnchantedMazeFactory : public MazeFactory { public: EnchantedMazeFactory(); virtual Room* MakeRoom(int n) const { return new EnchantedRoom(n, CastSpell()); } virtual Door* MakeDoor(Room* r1, Room* r2) const { return new DoorNeedingSpell(r1, r2); } protected: Spell* CastSpell() const; };
Abstract Factory – Class - BombedMazeFactory class BombedMazeFactory : public MazeFactory { public: BombedMazeFactory(); virtual Wall* MakeWall() const { return new BombedWall; } virtual Room* MakeRoom(int n) const { return new RoomWithABomb(n); } };
Abstract Factory – Using the Factory MazeGame game; BombedMazeFactory factory; game.CreateMaze(factory); • A “RoomWithABomb” can safely assume that its walls are “BombedWall”s if walls are built solely with a “BombedMazeFactory”.
Abstract Factory – Related Patterns • AbstractFactory classes are often implemented with factory methods, but they can also be implemented using Prototype • A concrete factory is often a singleton
Builder Object Creational
Builder • Intent • Separate the construction of a complex object from its representation so that the same construction process can create different representations
while(t=get the next token) { switch t.Type { CHAR: builder->ConvertCharacter(t.CHAR) FONT: builder->ConvertFontChange(t.FONT) PARA: builder->ConvertParagraph() } } Builder – Motivation (1) RTFReader builders TextConverter builder ParseRTF() ConvertCharacter(char)ConvertFontChange(Font)ConvertParagraph() ASCIIConverter TeXConverter TextWidgetConverter ConvertCharacter(char)GetASCIIText() ConvertCharacter(char)ConvertFontChange(Font)ConvertParagraph()GetTeXText () ConvertCharacter(char)ConvertFontChange(Font)ConvertParagraph()GetTextWidget() ASCIIText TeXText TextWidget
Builder – Motivation (2) • Each converter class is called a builder • The reader is called the director
for all objects in structure { builder->BuildPart()} Builder – Structure Director Builder builder Construct() BuildPart() ConcreteBuilder Product BuildPart()GetResult()
Builder – Collaborations aClient aDirector aConcreteBuilder new ConcreteBuilder new Director(aConcreteBuilder) Construct() BuildPartA() BuildPartB() BuildPartC() GetResult()
Builder - Consequences • It lets the programmer vary a product’s internal representation • It isolates code for construction and representation • It gives the programmer finer control over the construction process
Builder - Implementation • Assembly and construction interface • Why no abstract class for products? • Empty methods as default in Builder • The parent builder class (or topmost class in any hierarchy) need not be an abstract class. It can have default implementation of different virtual functions and act as a concrete class.
Builder – Class - MazeBuilder Ref: The UML model
Builder – Class - MazeBuilder class MazeBuilder { public: virtual void BuildMaze() { } virtual void BuildRoom(int room) { } virtual void BuildDoor(int roomFrom, int roomTo) { } virtual Maze* GetMaze() { return 0; } protected: MazeBuilder(); };
Builder – Class - MazeGame Maze* MazeGame::CreateMaze (MazeBuilder& builder) { builder.BuildMaze(); builder.BuildRoom(1); builder.BuildRoom(2); builder.BuildDoor(1, 2); return builder.GetMaze(); }
Builder – Class - MazeGame Maze* MazeGame::CreateComplexMaze (MazeBuilder& builder) { builder.BuildRoom(1); // … builder.BuildRoom(1001); return builder.GetMaze(); }
Builder – Class – StandardMazeBuilder (1) class StandardMazeBuilder : public MazeBuilder { public: StandardMazeBuilder(); virtual void BuildMaze(); virtual void BuildRoom(int); virtual void BuildDoor(int, int); virtual Maze* GetMaze(); private: Direction CommonWall(Room*, Room*); Maze* _currentMaze; };
Builder – Class – StandardMazeBuilder (2) StandardMazeBuilder::StandardMazeBuilder () { _currentMaze = 0; } void StandardMazeBuilder::BuildMaze () { _currentMaze = new Maze; } Maze* StandardMazeBuilder::GetMaze () { return _currentMaze; }
Builder – Class – StandardMazeBuilder (3) void StandardMazeBuilder::BuildRoom (int n) { if (!_currentMaze->RoomNo(n)) { Room* room = new Room(n); _currentMaze->AddRoom(room); room->SetSide(North, new Wall); room->SetSide(South, new Wall); room->SetSide(East, new Wall); room->SetSide(West, new Wall); } };
Builder – Class – StandardMazeBuilder (4) void StandardMazeBuilder::BuildDoor (int n1, int n2) { Room* r1 = _currentMaze->RoomNo(n1); Room* r2 = _currentMaze->RoomNo(n2); Door* d = new Door(r1, r2); r1->SetSide(CommonWall(r1, r2), d); r2->SetSide(CommonWall(r2, r1), d); };
Builder – Using the Builder Maze* maze; MazeGame game; StandardMazeBuilder builder; game.CreateMaze(builder); maze = builder.GetMaze();
Builder – Class – CountingMazeBuilder (1) class CountingMazeBuilder : public MazeBuilder { public: CountingMazeBuilder(); virtual void BuildMaze(); virtual void BuildRoom(int); virtual void BuildDoor(int, int); virtual void AddWall(int, Direction); void GetCounts(int&, int&) const; private: int _doors; int _rooms; };
Builder – Class – CountingMazeBuilder (2) CountingMazeBuilder:: CountingMazeBuilder () { _rooms = _doors = 0; } void CountingMazeBuilder::BuildRoom (int) { _rooms++; } void CountingMazeBuilder::BuildDoor (int, int) { _doors++; } void CountingMazeBuilder::GetCounts (int& rooms, int& doors) const { rooms = _rooms; doors = _doors; }
Builder – Using CountingMazeBuilder int rooms, doors; MazeGame game; CountingMazeBuilder builder; game.CreateMaze(builder); builder.GetCounts(rooms, doors); cout << “The Maze has ” << rooms << “ rooms and ” << doors << “ doors” << endl;
Builder Focuses on constructing a complex object step by step Returns the product as a final step Abstract Factory Emphasis is on families of product objects (either simple or complex) The product gets returned immediately Builder – Related Patterns A Composite is what the builder often builds
Factory Method Class Creational
Factory Method • Intent • Define an interface for creating an object, but let subclasses decide which class to instantiate • Factory Method lets a class defer instantiation to subclasses • Also Known As • Virtual Constructor
Document* doc = CreateDocument(); docs.Add(doc); doc->Open(); return new MyDocument; Factory Method - Motivation docs Document Application Open()Close() Save() Revert() CreateDocument()NewDocument()OpenDocument() MyDocument MyApplication CreateDocument()
product = FactoryMethod() return new ConcreteProduct Factory Method - Structure Creator FactoryMethod()AnOperation Product ConcreteProduct ConcreteCreator FactoryMethod()
Factory Method - Consequences • Clients might have to subclass the Creator class just to create a particular ConcreteProduct Object – a potential disadvantage • Connects parallel class hierarchies • See next slide for an example
Factory Method – Parallel Class Hierarchies Figure Client Manipulator CreateManipulator()… DownClick()Drag() UpClick() LineFigure TextManipulator CreateManipulator()… DownClick()Drag() UpClick() LineManipulator DownClick()Drag() UpClick() TextFigure CreateManipulator()…
Factory Method – Implementation • Create objects in a separate operation so that subclasses can override the way they’re created • Parameterized factory methods • Just be careful not to call factory methods in the Creator’s constructor – the factory method in the ConcreteCreator won’t be available yet • Use “lazy initialization” • Using templates to avoid subclassing