Last Updated on September 24, 2023 by KnownSense
The Prototype pattern is a creational design pattern that focuses on creating new objects by copying an existing object, called the prototype, rather than using constructors. This pattern is particularly useful when creating objects that are similar to existing ones, as it promotes code reusability and reduces the overhead of repeatedly initializing objects from scratch.
There are multiple scenarios where the Prototype pattern is useful:
- Creating objects with multiple configurations, where existing objects can be used as templates.
- Cloning objects with intricate initialization processes, such as those involving database connections or network calls.
- Designing frameworks where clients create their own classes by extending existing prototypes.
Benefits
- Reduced Overhead: Creating new objects by copying existing ones is often more efficient than initializing objects from scratch, especially if the initialization process is complex.
- Flexibility: The Prototype pattern supports creating new object instances at runtime, which can be useful when the exact type of object needed isn’t known until runtime.
- Code Reusability: Objects can be easily reused by cloning existing prototypes, reducing the need to create multiple constructor overloads for different object configurations.
- Customization: Cloned objects can be modified without affecting the original prototypes, allowing for customization while maintaining a common foundation.
- Hides Implementation Details: The Prototype pattern abstracts the cloning process and object creation from the client code, promoting separation of concerns.
UML for Prototype
Implementation
Imagine you’re developing a coffee shop application that allows customers to create customized coffee orders. Each coffee order might have multiple components like size, type of coffee, and additional toppings. Using the Prototype design pattern, you can create a prototype object for a basic coffee order and then clone it to create customized versions.
Step1: Create a interface Prototype with method getClone
public interface Prototype {
Prototype getClone();
}
Step2: Create a concrete class CoffeeOrder which implements the Protoype interface
public class CoffeeOrder implements Prototype {
private String size;
private String coffeeType;
private List<String> toppings;
// Constructor
public CoffeeOrder(String size, String coffeeType) {
this.size = size;
this.coffeeType = coffeeType;
this.toppings = new ArrayList<>();
}
// Method to add toppings
public void addTopping(String topping) {
toppings.add(topping);
}
public String getSize() {
return size;
}
public String getCoffeeType() {
return coffeeType;
}
public List<String> getToppings() {
return toppings;
}
//Clone method
@Override
public Prototype getClone() {
CoffeeOrder clonedOrder = new CoffeeOrder(size,coffeeType);
//Deep copy the toppings list
clonedOrder.toppings = new ArrayList<>(this.toppings);
return clonedOrder;
}
}
Step3: In main class, create a prototype coffee order, and clone the prototype to create customized orders
public class CoffeeShopApp {
public static void main(String[] args) {
// Create a prototype coffee order
CoffeeOrder prototypeOrder = new CoffeeOrder("Medium", "Latte");
prototypeOrder.addTopping("Whipped Cream");
// Clone the prototype to create customized orders
CoffeeOrder order1 = (CoffeeOrder) prototypeOrder.getClone();
order1.addTopping("Caramel Drizzle");
CoffeeOrder order2 = (CoffeeOrder) prototypeOrder.getClone();
order2.addTopping("Chocolate Syrup");
// Display the orders
System.out.println("Order 1: " + order1.getSize() + " " + order1.getCoffeeType() +
" with " + order1.getToppings());
System.out.println("Order 2: " + order2.getSize() + " " + order2.getCoffeeType() +
" with " + order2.getToppings());
}
}
OUTPUT:
Order 1: Medium Latte with [Whipped Cream, Caramel Drizzle]
Order 2: Medium Latte with [Whipped Cream, Chocolate Syrup]
Note: In above example instead of creating Prototype interface, we can use interface java.lang.Cloneable.
Conclusion
The Prototype design pattern in Java offers efficient object creation through cloning, enabling customization and reducing complexity. It involves implementing Cloneable
and clone()
to create copies of objects, making it useful for managing complex object hierarchies and maintaining isolated instances with options for deep or shallow copying.