Prototype Pattern
Introduction
The Prototype pattern is a creational design pattern that involves creating new objects by copying an existing object, known as the prototype. This pattern is particularly useful when the cost of creating an object is more expensive or complex than copying an existing one.
Purpose
- To create new objects by duplicating an existing object, which serves as a prototype.
- To reduce the need for subclassing by cloning objects.
Use Cases
- When the classes to instantiate are specified at runtime.
- To avoid building a class hierarchy of factories that parallels the class hierarchy of products.
- When instances of a class can have only a few different combinations of state.
Advantages/Disadvantages
Advantages:
- Adds and removes products at runtime.
- Specifies new objects by varying values.
- Reduces the need for creating subclasses.
Disadvantages:
- Each subclass of Prototype must implement the cloning method.
- Cloning complex objects that have circular references might be very tricky.
Implementation in JavaScript
class Prototype {
clone() {
throw new Error('You must implement the clone method');
}
}
class ConcretePrototype extends Prototype {
constructor(field) {
super();
this.field = field;
}
clone() {
return new ConcretePrototype(this.field);
}
}
// Usage
const original = new ConcretePrototype('value');
const clone = original.clone();
console.log(original.field); // "value"
console.log(clone.field); // "value", cloned from original
Practical Example
Suppose we have a logging system that needs different types of loggers (e.g., for errors, warnings, info). These loggers share common properties but differ slightly. We can use the Prototype pattern for easy duplication and customization.
class LoggerPrototype {
constructor(type, level) {
this.type = type;
this.level = level;
}
clone() {
return new LoggerPrototype(this.type, this.level);
}
log(message) {
console.log(`[${this.type} - ${this.level}]: ${message}`);
}
}
// Usage
const errorLoggerPrototype = new LoggerPrototype('Error', 'High');
const warningLoggerPrototype = new LoggerPrototype('Warning', 'Medium');
// Cloning prototypes for specific situations
const systemErrorLogger = errorLoggerPrototype.clone();
const fileWarningLogger = warningLoggerPrototype.clone();
systemErrorLogger.log('System failure!'); // [Error - High]: System failure!
fileWarningLogger.log('File not found.'); // [Warning - Medium]: File not found.
In this practical example, LoggerPrototype
is used to create different logger prototypes, which are then cloned to create specific loggers for various situations. This approach simplifies the creation of objects that have similar configurations but different data.
Next, we will discuss the Adapter pattern.