Reading Time: 3 minutesPrototype Design Pattern in Python
Prototype Design Pattern in Python
Design Patterns Home
What is it?
The Prototype Design Pattern involves creating a basic object, called the prototype. This prototype has default values for its attributes. Then, in order to make customized objects, this prototype is cloned, with altered values for its attributes. Take example of a Car. The prototypical Car object has, say, attributes: make='Honda', color='Blue', enginePower='2000cc'. This prototype can be used to create different cars of variable makes, colors and engine powers. The pattern provides a convenient way of configuring basic attributes of the prototype to make individual objects.
It is classified under Creational Design Patterns, since it provides a widely accepted method of instantiating classes.
Why the need for it: Problem Statement
There is no real "need" for the Prototype Pattern per se, but the Prototype Pattern does provide an organized method of creating customizable objects. The prototype serves as a foundation for each object, allowing the user to give it a unique identity by tweaking the attributes copied from prototype. The Prototype Pattern enables the user to create several identical objects, which is an expensive process otherwise.
Terminology
- Prototype Class: A special class 'Prototype', having methods to register/unregister to-be-cloned objects and clone these objects. These are briefly explained below:
- __init__: The __init__ method creates a dictionary object which stores to-be-cloned objects.
- registerObject: The register method registers the object to be cloned. In our example, this is an object of the class Car. The register method takes two arguments, 'name' & 'obj'. These values denote the key-value pair which will be entered in the dictionary that contains the to-be-cloned objects. In our example, we register a Car object and give it a name of 'basicCar'.
- unregisterObject: Deletes the mentioned to-be-cloned object from the dictionary.
- clone: The clone method clones/replicates the prototypical object. It provides a way of updating the basic attributes of the basic object. It returns the cloned object.
- Prototypical Class: Class denoting the object whose variations will be made. In our example, this is the class Car.
- Prototypical object: An object of the Prototypical class that will be supplied to the registerObject() method of the Prototype class with a 'name'.
- Cloned object: A variation of the primary product, having its own identity. It is obtained by calling the clone() method of the prototype with the above 'name' as its argument.
Pseudo Code
def registerObject(name, obj): |
def unregisterObject(name): |
def clone(name, * * kwargs): |
create our prototypical object i.e. an object of prototypical class Car |
create an instance of the prototype class , say 'defaultCar' |
register the to - be - cloned object i.e defaultCar; to do this, call the registerObject() method, provide a name to it, say, 'basicCar' , and the second argument is the prototypical object i.e. 'defaultCar' |
to clone the object 'basicCar' , call the clone method and assign it to a new variable; optionally, we can provide keyword arguments if we wish to change the basic attributes. |
use the cloned object to suit your needs |
How to implement it
return '{} | {} | {}' . format ( self .engine, self .color, self .seats) |
self ._toBeClonedObjects = {} |
def registerObject( self , name, obj): |
self ._toBeClonedObjects[name] = obj |
def unregisterObject( self , name): |
del self ._toBeClonedObjects[name] |
def clone( self , name, * * kwargs): |
clonedObject = copy.deepcopy( self ._toBeClonedObjects.get(name)) |
clonedObject.__dict__.update(kwargs) |
prototype.registerObject( 'basicCar' , defaultCar) |
carOne = prototype.clone( 'basicCar' ) |
print ( "Details of carOne:" , carOne) |
carTwo = prototype.clone( 'basicCar' , color = "Black" ) |
print ( "Details of carTwo:" , carTwo) |
Walkthrough of implementation
- First, we create our prototypical object i.e. an object of prototypical class Car.
- Then, we create an instance of the prototype class.
- Then, we register the to-be-cloned object i.e defatulCar. To do this, we call the registerObject() method, provide a name to it, 'basicCar', and the second argument is the prototypical object i.e. defaultCar.
- To clone the object 'basicCar', we call the clone method and assign it to a new variable. Optionally, we can provide keyword arguments if we wish to change the basic attributes.
A word on deep copy
There are three ways to copy objects in Python: assignment, shallow copy & deep copy.
-
- Normal assignment points the new variable towards the original object. Let's use the builtin id() function prove this. The id() function returns the object's memory address.
objThree = [objOne, objTwo] |
print ( id (objThree) = = id (objFour) ) |
print ( id (objThree[ 0 ]) = = id (objFour[ 0 ]) ) |
-
- Shallow copy
The difference between shallow and deep copying is only relevant for compound objects (objects that contain other objects, like lists or class instances):
A shallow copy constructs a new compound object and then (to the extent possible) inserts references into it to the objects found in the original.
A deep copy constructs a new compound object and then, recursively, inserts copies into it of the objects found in the original.
Python Documentation
objFour = copy.copy(objThree) |
print ( id (objThree) = = id (objFour) ) |
print ( id (objThree[ 0 ]) = = id (objFour[ 0 ]) ) |
-
- Deep Copy
objFour = copy.deepcopy(objThree) |
print ( id (objThree) = = id (objFour) ) |
print ( id (objThree[ 0 ]) = = id (objFour[ 0 ]) ) |
Related to: Abstract Factory
See also: