Prototype Design Pattern
Specify the kind of objects to create using a prototypical instance, and create new objects by copying this prototype. (dofactory)
- Prototype declares an interface for cloning itself
- ConcretePrototype implements an operation for cloning itself
- Client creates a new object by asking a prototype to clone itself
Using a prototype manager
When the application uses a lot of prototypes that can be created and destroyed dynamically, a registry of available prototypes should be kept. This registry is called the prototype manager and it should implement operations for managing registered prototypes like registering a prototype under a certain key, searching for a prototype with a given key, removing one from the register, etc. The clients will use the interface of the prototype manager to handle prototypes at run-time and will ask for permission before using the Clone() method. There is not much difference between an implementation of a prototype which uses a prototype manager and a factory method implemented using class registration mechanism. Maybe the only difference consists in the performance. Prototype Manager – implemented usually as a hashtable keeping the object to clone. When use it, prototype become a factory method which uses cloning instead of instantiation.
Implementing the Clone operation
A small discussion appears when talking about how deep or shallow a clone should be: a deep clone clones the instance variables in the cloning object while a shallow clone shares the instance variables between the clone and the original. Usually, a shallow clone is enough and very simple, but cloning complex prototypes should use deep clones so the clone and the original are independent, a deep clone needing its components to be the clones of the complex object’s components. When we clone complex objects which contains other objects, we should take care how they are cloned. We can clone contained objects also (deep cloning) or we can the same reference for them, and to share them between cloned container objects.
There are cases when the internal states of a clone should be initialized after it is created. This happens because these values cannot be passed to the Clone() method, that uses an interface which would be destroyed if such parameters were used. In this case the initialization should be done by using setting and resetting operations of the prototype class or by using an initializing method that takes as parameters the values at which the clone’s internal states should be set.
- Prototypes are useful when object initialization is expensive, and you anticipate few variations on the initialization parameters. In this context, Prototype can avoid expensive “creation from scratch”, and support cheap cloning of a pre-initialized prototype.
- Use Prototype Pattern when a system should be independent of how its products are created, composed, and represented, and:
- Classes to be instantiated are specified at run-time
- Avoiding the creation of a factory hierarchy is needed
- It is more convenient to copy an existing instance than to create a new one.
- Objects are required that are similar to existing objects.
- Prototype co-opts one instance of a class for use as a breeder of all future instances.
- Prototype is unique among the other creational patterns in that it does not require a class – only an object. Object-oriented languages like Self and Omega that do away with classes completely rely on prototypes for creating new objects.
- It allows an object to create customized objects without knowing their class or any details of how to create them.
- The process of copying an object can be complicated.
- Classes that have circular references to other classes are difficult to clone.
- Overuse of the pattern could affect performance, as the prototype object itself would need to be instantiated if you use a registry of prototypes.
- Abstract Factory might store a set of Prototypes from which to clone and return product objects. Abstract Factory classes are often implemented with Factory Methods, but they can be implemented using Prototype.
- Factory Method creation is through inheritance but Protoype creation is through delegation. Prototype does not require subclassing, but it does require an “initialize” operation. Factory Method requires subclassing, but does not require Initialize.
- Often, designs start out using Factory Method (less complicated, more customizable, subclasses proliferate) and evolve toward Abstract Factory, Protoype, or Builder (more flexible, more complex) as the designer discovers where more flexibility is needed.
- Designs that make heavy use of the Composite and Decorator patterns often can benefit from Prototype as well.