Prototype

Prototypes create new objects by copying existing objects instead of creating new instances from scratch; often used due to its efficiency for complex object creation.

TL;DR

Create an object based on an existing object through cloning. It allows you to simplify the creation of an object by using an existing one rather than going through the trouble of setting it up from scratch.

Problem

Suppose you want to create an exact copy of an object. The naive solution is to create an object of the same class, add all the fields from the original object, and hope you get an exact copy.

This breaks down, however, if there are private fields that are not visible from outside the object itself - especially if the object is keeping some sort of state.

Additionally, since we have to know which class we are cloning, the code we write becomes dependent on that class.

Solution

To use the Prototype pattern we can add a clone method to the class that is being cloned; this simply establishes a common interfaces for all objects that support cloning.

clone is fairly similar in all classes: it creates an object of the current class and carries over all the field values from the old one into the new one (including private fields, if required). This is particularly useful when objects can have dozens of fields and hundreds of possible configurations

Note

In addition to a basic prototype implementation, we can also create a prototype registry for frequently-used prototypes. This stores a set of pre-build objects that are ready to be copied (often in some sort of name-based hash map)

Note

Although the implementation of prototypes suggests the creation of a clone method in the interface, some languages, such as Python, already provide copy functionality (Python uses copy.copy() and copy.deepcopy(), PHP uses clone keywords, etc).

Caution

When using Python, copy.copy() provides a shallow copy - that it, with new object references to nested objects are shared and only the top level of the object is copied. Changes to nested or mutable objects affect both the original and the copy.

copy.deepcopy() is a recursive version of copying that copies all nested objects and creates new references for them, such that any change between originals and copies don’t affect each other.

import copy
 
class Sheep:
    def __init__(self, name: str, category: str = 'Mountain Sheep'):
        self._name = name
        self._category = category
    
    def set_name(self, name: str):
        self._name = name
    
    def get_name(self) -> str:
        return self._name
    
    def set_category(self, category: str):
        self._category = category
    
    def get_category(self) -> str:
        return self._category
 
# Create original sheep
original = Sheep('Jolly')
print(original.get_name())      # Jolly
print(original.get_category())  # Mountain Sheep
 
# Clone and modify what is required
cloned = copy.copy(original)
cloned.set_name('Dolly')
print(cloned.get_name())        # Dolly
print(cloned.get_category())    # Mountain Sheep