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. |
