1. Introduction
It specifies the kinds of objects to create using a prototypical instance and creates new objects by copying this prototype.
This pattern offers an alternative approach to creating new objects by duplicating or cloning an existing instance, rather than building a new one from scratch. This allows you to bypass the overhead associated with constructing a brand-new object. Why is this approach valuable? There are several reasons:
- Some resources required for creating an object may be scarce or difficult to obtain.
- Constructing a new instance can be a time-intensive process.
The key idea behind this pattern, as highlighted in the GoF definition, is to create a new object by using an existing object as a template.
In practice, this pattern typically involves an abstract class or interface, known as the prototype, which defines a cloning method. Concrete implementations of the prototype provide the actual cloning behavior. Clients can then create new objects by requesting a prototype instance to clone itself.
2. Key Concepts
- Prototype:
- An interface or abstract class that declares the cloning method.
- Used as a template for creating new objects.
- Concrete Prototype:
- Implements the Prototype interface and provides the actual implementation of the cloning method.
- Client:
- Uses the prototype to create new objects by invoking the cloning method.
3. Steps to Implement the Prototype Pattern
Step 1: Define the Prototype Interface
The prototype interface declares the method for cloning objects. In Java, this can be done using the Cloneable
interface.
public interface Prototype extends Cloneable { Prototype clone(); }
Step 2: Create Concrete Prototypes
Concrete prototypes implement the cloning logic and define the actual objects that can be cloned.
public class Circle implements Prototype { private int radius; public Circle(int radius) { this.radius = radius; } public int getRadius() { return radius; } public void setRadius(int radius) { this.radius = radius; } @Override public Circle clone() { return new Circle(this.radius); } @Override public String toString() { return "Circle with radius: " + radius; } } public class Rectangle implements Prototype { private int length; private int width; public Rectangle(int length, int width) { this.length = length; this.width = width; } public int getLength() { return length; } public void setLength(int length) { this.length = length; } public int getWidth() { return width; } public void setWidth(int width) { this.width = width; } @Override public Rectangle clone() { return new Rectangle(this.length, this.width); } @Override public String toString() { return "Rectangle with length: " + length + ", width: " + width; } }
Step 3: Create the Client
The client interacts with prototypes to create new objects by cloning.
public class PrototypePatternDemo { public static void main(String[] args) { // Create initial objects Circle originalCircle = new Circle(10); Rectangle originalRectangle = new Rectangle(20, 30); // Clone the objects Circle clonedCircle = originalCircle.clone(); Rectangle clonedRectangle = originalRectangle.clone(); // Modify the cloned objects clonedCircle.setRadius(15); clonedRectangle.setLength(25); // Display the objects System.out.println("Original Circle: " + originalCircle); System.out.println("Cloned Circle: " + clonedCircle); System.out.println("Original Rectangle: " + originalRectangle); System.out.println("Cloned Rectangle: " + clonedRectangle); } }
Output
Original Circle: Circle with radius: 10 Cloned Circle: Circle with radius: 15 Original Rectangle: Rectangle with length: 20, width: 30 Cloned Rectangle: Rectangle with length: 25, width: 30
4. Key Benefits
- Reduced Object Creation Cost: Avoids the overhead of creating new objects from scratch, especially if initialization is resource-intensive.
- Simplified Object Creation: Instead of writing complex object creation logic repeatedly, cloning provides a straightforward way to duplicate objects.
- Supports Runtime Configuration: Allows dynamic creation of objects at runtime, which is helpful in scenarios where object configurations are not predefined.
- Encapsulation of Object State: By cloning, the client doesn’t need to know the details of how an object is constructed.
5. Use Cases
- Game Development: Creating multiple enemy or character objects with slight variations.
- Prototyping in GUI Applications: Duplicating user interface elements (e.g., buttons, windows) with minor modifications.
- Database Connections: Cloning connection objects for reuse, avoiding the overhead of establishing new connections.
- Document Editors: Copying existing documents, shapes, or text with minor tweaks.
6. Comparison with Other Patterns
Aspect | Prototype Pattern | Factory Method Pattern |
Object Creation | Clones an existing object. | Creates new objects through factory methods. |
Use Case | When creating an object is costly or complex. | When object creation depends on subclasses. |
Complexity | Simpler for cloning; avoids repetitive initialization. | More complex due to the need for subclassing. |