Prototype
Why
Prototype:
Need to duplicate
objects with different dynamic types
Prototype is
particularly useful with static languages like C++, where classes are not
objects, and little or no type information is available at run-time.
Benefits:
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.
Difficulty:
The implementation of clone operation:
especially when their internals include objects that don't support copying or
have circular references.
Implementation
Solutions:
-
Provide a polymorphic method that
returns an instance of the same type as the object on which the method is
called
-
Polymorphic method calls copy constructor, returns
base class pointer or reference to concrete derived type
-
Issues have to be considered
1. Using a prototype manager.
When the number of prototypes in a system isn't fixed
(that is, they can be created and destroyed dynamically), keep a registry
of available prototypes. A prototype manager is an
associative store that returns the prototype matching a given key. It
has operations for registering a prototype under a key and for unregistering
it. Clients can change or even browse through the registry at run-time.
2. Implementing the Clone operation.
The
default copy constructor in C++ does a member-wise copy, which means pointers will be
shared between the copy and the original. Cloning
prototypes with complex
structures usually requires a deep copy,
because the clone and the original must be independent.
3. Initializing clones.
Sample
Code
a MazePrototypeFactory subclass of the MazeFactory class.
class MazePrototypeFactory : public
MazeFactory {
public:
MazePrototypeFactory(Maze*,
Wall*, Room*, Door*);
virtual
Maze* MakeMaze() const;
virtual
Room* MakeRoom(int) const;
virtual
Wall* MakeWall() const;
virtual
Door* MakeDoor(Room*, Room*) const;
private:
Maze*
_prototypeMaze;
Room*
_prototypeRoom;
Wall*
_prototypeWall;
Door*
_prototypeDoor;
};
MazePrototypeFactory::MazePrototypeFactory
( Maze* m, Wall* w, Room* r, Door* d ) {
_prototypeMaze
= m;
_prototypeWall
= w;
_prototypeRoom
= r;
_prototypeDoor
= d;
}
Wall* MazePrototypeFactory::MakeWall
() const {
return
_prototypeWall->Clone();
}
Door* MazePrototypeFactory::MakeDoor
(Room* r1, Room *r2) const {
Door* door
= _prototypeDoor->Clone();
door->Initialize(r1,
r2);
return
door;
}
MazeGame game;
MazePrototypeFactory
simpleMazeFactory(
new Maze,
new Wall, new Room, new Door
);
Maze* maze =
game.CreateMaze(simpleMazeFactory);
To
change the type of maze, we initialize MazePrototypeFactory
with a different set of prototypes. The following call creates a maze with a BombedDoor and a RoomWithABomb:
MazePrototypeFactory
bombedMazeFactory(
new Maze,
new BombedWall, new RoomWithABomb, new Door
);
Note: An object
that can be used as a prototype, such as an instance of Wall, must
support the Clone operation. It must also
have a copy constructor for cloning. It may also need a
separate operation for reinitializing internal state.
Revised for prototype
class Door : public MapSite {
public:
Door();
virtual
void Initialize(Room*, Room*); // a separate operation for
reinitializing internal state
virtual
Door* Clone() const; // support the Clone
operation
virtual
void Enter();
Room*
OtherSideFrom(Room*);
private:
Room*
_room1;
Room*
_room2;
};
// a copy constructor for cloning
Door::Door (const Door& other) {
_room1 =
other._room1;
_room2 =
other._room2;
}
// a separate operation for
reinitializing internal state
void Door::Initialize (Room* r1, Room*
r2) {
_room1 =
r1;
_room2 =
r2;
}
// support the Clone
operation
Door* Door::Clone () const {
return new
Door(*this);
}
Comments
Post a Comment