Prototype Pattern
Creates new objects by copying an existing object, known as the prototype. This pattern is useful when creating an instance of a class is expensive or complicated. Instead of constructing a new object from scratch, you clone an existing one and modify it as needed.
✦ Problem
Sometimes you need to create an exact copy of an object. The straightforward approach—creating a new object and copying all field values—breaks when some fields are private or the object's class is unknown to you (you only have an interface reference). Depending on concrete classes creates unwanted coupling.
✦ Solution
Delegate the cloning process to the actual objects being cloned. The pattern declares a common interface for all clonable objects (usually a single clone method). An object that supports cloning is called a prototype. When you need a copy, you call the prototype's clone method instead of instantiating a new object with the new operator.
Participants
| Role | Responsibility |
|---|---|
| Prototype | Declares an interface for cloning itself |
| ConcretePrototype | Implements the cloning operation, returning a copy of itself |
| Client | Creates a new object by asking a prototype to clone itself |
TypeScript Example
interface Cloneable<T> {
clone(): T;
}
class ComponentStyle implements Cloneable<ComponentStyle> {
constructor(
public fontSize: number,
public color: string,
public backgroundColor: string,
public borderRadius: number,
public padding: number,
) {}
clone(): ComponentStyle {
return new ComponentStyle(
this.fontSize,
this.color,
this.backgroundColor,
this.borderRadius,
this.padding,
);
}
}
const baseStyle = new ComponentStyle(16, "#fff", "#1a1a2e", 8, 12);
const headerStyle = baseStyle.clone();
headerStyle.fontSize = 24;
headerStyle.backgroundColor = "#16213e";
const alertStyle = baseStyle.clone();
alertStyle.color = "#ff4444";
alertStyle.borderRadius = 16;
console.log(baseStyle);
console.log(headerStyle);
console.log(alertStyle);Real-World Example
Spreadsheet applications use the Prototype pattern when you copy a cell with complex formatting—fonts, colors, borders, formulas. The application clones the existing cell object rather than re-constructing all that formatting from scratch. Similarly, game engines clone enemy templates, graphic editors duplicate shapes with all their properties, and JavaScript's Object.create() is built on the prototype concept.
✓ When to Use
- •Creating an object is more expensive than copying an existing one
- •You need to instantiate classes that are specified at runtime (dynamic loading)
- •Your system should be independent of how its products are created
- •You want to keep the number of classes to a minimum and define new objects by varying values rather than defining new subclasses
✗ When Not to Use
- •Objects are simple and cheap to create from scratch
- •Deep cloning is required but the object graph contains circular references making cloning complex
- •Objects contain fields that reference external resources (file handles, network connections) that shouldn't be duplicated
Understanding the Prototype Pattern
The Prototype pattern is one of the most important creational design patterns in object-oriented software design. Originally cataloged by the Gang of Four (Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides) in their seminal 1994 book "Design Patterns: Elements of Reusable Object-Oriented Software," this pattern continues to be a cornerstone of modern software architecture.
Creates new objects by copying an existing object, known as the prototype. This pattern is useful when creating an instance of a class is expensive or complicated. Instead of constructing a new object from scratch, you clone an existing one and modify it as needed. The pattern involves the following key participants: Prototype, ConcretePrototype, Client. Each participant has a well-defined role, and the interactions between them create a flexible, maintainable structure that can evolve with changing requirements.
In TypeScript and modern JavaScript development, the Prototype pattern is particularly valuable because the language's type system and interface support allow you to express the pattern with compile-time safety. TypeScript generics can make Prototype implementations even more flexible and reusable while maintaining strong type checking throughout the pattern's structure.
Knowing when to apply the Prototype pattern is as important as knowing how. Common use cases include: Creating an object is more expensive than copying an existing one; You need to instantiate classes that are specified at runtime (dynamic loading); Your system should be independent of how its products are created. However, overusing design patterns can lead to unnecessarily complex code. Always evaluate whether the pattern genuinely solves your problem or if a simpler approach would suffice.
The Prototype pattern is related to several other design patterns: Singleton, Factory Method, Abstract Factory, Memento. Understanding these relationships helps you choose the right pattern for your specific situation and combine patterns effectively when building complex systems.
Related Patterns
Ensures a class has only one instance and provides a global point of access to it. The Singleton pattern restricts insta…
Defines an interface for creating an object, but lets subclasses decide which class to instantiate. Factory Method lets …
Provides an interface for creating families of related or dependent objects without specifying their concrete classes. A…
Captures and externalizes an object's internal state so it can be restored later, without violating encapsulation. Memen…
More Creational Patterns
Explore All Design Patterns
Browse our complete reference of 23 design patterns with TypeScript examples, UML participants, and practical guidance.