Prototype is a creational design pattern that allows cloning objects, even complex ones, without coupling to their specific classes.
All prototype classes should have a common interface that makes it possible to copy objects even if their concrete classes are unknown. Prototype objects can produce full copies since objects of the same class can access each other’s private fields.
Complexity:
Popularity:
Usage examples: The Prototype pattern is available in TypeScript out of the box with a JavaScript’s native Object.assign()
method.
Identification: The prototype can be easily recognized by a clone
or copy
methods, etc.
Conceptual Example
This example illustrates the structure of the Prototype design pattern and focuses on the following questions:
What classes does it consist of?
What roles do these classes play?
In what way the elements of the pattern are related?
index.ts: Conceptual example
/**
* The example class that has cloning ability. We'll see how the values of field
* with different types will be cloned.
*/
class Prototype {
public primitive: any;
public component: object;
public circularReference: ComponentWithBackReference;
public clone(): this {
const clone = Object.create(this);
clone.component = Object.create(this.component);
// Cloning an object that has a nested object with backreference
// requires special treatment. After the cloning is completed, the
// nested object should point to the cloned object, instead of the
// original object. Spread operator can be handy for this case.
clone.circularReference = {
...this.circularReference,
prototype: { ...this },
};
return clone;
}
}
class ComponentWithBackReference {
public prototype;
constructor(prototype: Prototype) {
this.prototype = prototype;
}
}
/**
* The client code.
*/
function clientCode() {
const p1 = new Prototype();
p1.primitive = 245;
p1.component = new Date();
p1.circularReference = new ComponentWithBackReference(p1);
const p2 = p1.clone();
if (p1.primitive === p2.primitive) {
console.log('Primitive field values have been carried over to a clone. Yay!');
} else {
console.log('Primitive field values have not been copied. Booo!');
}
if (p1.component === p2.component) {
console.log('Simple component has not been cloned. Booo!');
} else {
console.log('Simple component has been cloned. Yay!');
}
if (p1.circularReference === p2.circularReference) {
console.log('Component with back reference has not been cloned. Booo!');
} else {
console.log('Component with back reference has been cloned. Yay!');
}
if (p1.circularReference.prototype === p2.circularReference.prototype) {
console.log('Component with back reference is linked to original object. Booo!');
} else {
console.log('Component with back reference is linked to the clone. Yay!');
}
}
clientCode();
Output.txt: Execution result
Primitive field values have been carried over to a clone. Yay!
Simple component has been cloned. Yay!
Component with back reference has been cloned. Yay!
Component with back reference is linked to the clone. Yay!
Prototype in Other Languages