Prototype is a creational design pattern that let you produce new objects by copying existing ones.
How do we copy an object? First, we create a fresh object of the same class. Then we go over all object fields and copy their values to the new object.
Nice! But there's a catch. Not all objects can be copies this way. Some of the objects can have private fields not accessible from the outside.
There's one more problem with this approach. Since you need to know about all object fields, your code will become tightly coupled to all concrete classes of these objects.
The Prototype pattern delegates the cloning to the objects themselves.
It defines a common interface for all objects that allow cloning. It allows cloning objects without tight coupling to their concrete classes. Usually, the prototype interface contains a single
The implementation of
clone method is similar for all classes. The method creates an object of a current class and copies all its field values into the new object. Most of the programming languages allow accessing private fields in an object of the same class, so the copying process is straightforward.
Objects that can be cloned are called prototypes. Sometimes, especially when your objects have a ton of fields and hundreds of possible configurations, prototypes can serve as an alternative to subclassing. In this case, the program creates a bunch of prototypes beforehand and then clones them instead of reconstructing objects from scratch.
Mitotic division of a cell
In industrial production, product prototypes are used to perform various tests before launching production of the main batch. However, in this case, prototypes don't participate in the subsequent production, playing a passive role.
Mitotic cell division (biology) is a much closer example for this pattern. After such division, two completely identical cells are formed. The original cell acts as a prototype and takes an active role in creating a copy.
A long time ago in a galaxy far, far away, the Republic used the DNA of a notorious mercenary to create an entire army of clones. All clones turned out to be the same, so it was easy to unify the tactical training, equipment, arms and transport.
P.S. A treat for Star Wars fans.
Prototype registry implementation
Prototype defines a cloning interface. In most cases, a single
clonemethod will be enough.
Concrete prototype implements the cloning method. In addition to straightforward copying field values to a fresh clone, this method may solve some caveats, which should remain hidden for Clients. For example, cloning linked objects, untangling recursive dependencies, etc.
Client uses the Prototype interface to retrieve an object's clone.
Prototype registry (optional) stores predefined set of prototypes. Provides an easy way to find a particular prototype. Usually, it can be implemented with a simple
name → prototypehash map.
In this example, the Prototype allows cloning geometric shape objects. All shape classes implement the common cloning interface with a single cloning method. Subclasses call the parent cloning method and then copy their own fields to the resulting object.
Therefore, the Prototype design pattern allows client code to clone objects, even without knowing and independent of their specific classes.
// Base prototype. abstract class Shape is field X: int field Y: int field color: string // A fresh object is initialized with values from the old object in // the constructor. method Shape(target: Shape) is if (target != null) then this.X = target.X; this.Y = target.Y; this.color = target.color; // Clone operation always returns one of the Shape subclasses. abstract method clone(): Shape // Concrete prototype. Cloning method creates a new object and passes itself to // the constructor. Until constructor is finished, has a reference to a fresh // clone. Therefore, nobody has access to a partly built clone. This helps to // make the cloning result consistent. class Rectangle extends Shape is field width: int field height: int method Rectangle(target: Rectangle) is // Parent constructor call is mandatory in order to copy private fields // defined in parent class. super(target) if (target != null) then this.width = target.width; this.height = target.height; method clone(): Shape is return new Rectangle(this) class Circle extends Shape is field radius: int method Circle(target: Circle) is super(target) if (target != null) then this.radius = target.radius; method clone(): Shape is return new Circle(this) // Somewhere in client code. class Application is field shapes: array of Shape method constructor() is Circle circle = new Circle(); circle.X = 10 circle.Y = 20 circle.radius = 15 shapes.add(circle); Circle anotherCircle = circle.clone(); shapes.add(anotherCircle); // anotherCircle is the exact copy of circle. Rectangle rectangle = new Rectangle(); rectangle.width = 10 rectangle.height = 20 shapes.add(rectangle); method businessLogic() is // Prototype rocks because it allows producing an object copy without // knowing anything about its type. Array shapesCopy = new Array of Shapes. // For instance, we don't know exact types of elements in shapes array. // We know they all of them are Shapes, that's all. But thanks to // polymorphism, when we call a clone method, it runs the method defined // in the class of that object. That's why we get proper clones instead // of the set of simple Shape objects. foreach shapes as shape do shapesCopy.add(shape.clone()) // shapesCopy will contain exact copies of shape array children.
When your code should not depend on the concrete product classes. For examples, when a concrete product type is unknown or can be changed in runtime.
The Prototype pattern provides a client an interface to work with all prototypes. This interface is common for all products.
It makes the client code independent from the concrete classes of the products it clones.
When you want to reduce the size of a product hierarchy that consists of similar products, configured in different ways (in other words, each class would have unique field values).
The Prototype pattern allows creating a set of prototype objects that represent all possible product variants.
Then, instead of creating objects directly, client code would retrieve an appropriate prototype and clone it.
How to Implement
cloneoperation to your product hierarchy.
If there's no such hierarchy, then create a cloning interface and define the
cloneoperation in it. Then implement this interface in all of your products.
Create a "registry" of prototype objects. It could contain objects of the same class, configured in different ways. You can put this registry into either a new factory class or a factory method inside a base product class.
The factory method should search for appropriate prototype based on the description that client code passes into its parameters. It could be either just a string tag or a complex set of search criteria. After appropriate prototype had been found, it should clone it and return the copy to the client.
Replace all direct constructor calls with the calls to the factory method.
Pros and Cons
- Allows cloning objects without coupling to their concrete classes.
- Reduces repeating initialization code.
- Creates complex objects faster.
- Provides an alternative for subclassing when you're dealing with complex objects having lots of configuration options.
- Complex objects with lots of references to other objects can be difficult to clone.
Relations with Other Patterns
Often, designs start out using Factory Method (less complicated, more customizable via subclasses) and evolve toward Abstract Factory, Prototype, or Builder (more flexible, but more complex) as the designer discovers where more flexibility is needed.
Prototype can be the simpler alternative to the Memento, if the object, which state you want to store in history, is fairly straightforward, doesn't have links to external resources, or if the links are easy to re-establish.