The Prototype Design Pattern is used for cloning an existing object, if the cost of creating a new object of a class is complicated and resource expensive.
Here, we do not have to repeat the complex object building process to get new object of a class. This design pattern comes under creational design pattern as it provides one of the best ways to create clone of complex objects.
Structure of the Prototype Design Pattern
- Prototype Interface or Abstract Class : This interface or abstract class declares the method for cloning itself. It acts as the common interface for all concrete prototypes.
- Concrete Prototype Classes : These classes implement the prototype interface or extend the prototype abstract class. They provide the actual implementation of the cloning method.
- Client : The client code creates new objects by cloning an existing prototype. The client is unaware of the specific concrete prototype classes and only interacts with the prototype interface or abstract class.
Advantages of Prototype Pattern
- Dynamic Object Creation : The pattern enables the runtime dynamic creation of new objects. Without being limited to particular actual classes, clients can request the cloning of prototypes to create new objects.
- Reduced Cost of Object Creation : Creating an instance of an object from scratch is frequently more inefficient than cloning an existing one. The Prototype design lowers the cost of creating an object, particularly in cases where resource or process startup is complicated.
- Customization through Cloning : Following the cloning procedure, clients can change the characteristics or states of the cloned objects to suit their needs. This offers an adaptable method for producing item modifications.
- Avoidance of Subclassing : The Prototype pattern does not rely on the development of subclasses in order to generate new objects, in contrast to some other creational patterns as the Factory Method or Abstract Factory. A more simple and scalable class structure may result from this.
When we should use Prototype pattern
- When the logic of creating a new object is complex and it requires resource extensive operations to be performed.
- When we want to hide the logic of object creation and its representation from client.
- When the classes to instantiate are specified at run-time.
- When objects of a class can have only few different state(lets say N). It is more convenient to first create N prototype object of different states and clone them later when we need objects of different states.
Implementation of Prototype Design Pattern
We will create an abstract class Bird and it's concrete implementation Parrot.java, Sparrow.java and Eagle.java. Each concrete implementation of Bird class will override cloneObject method.
Bird.javapublic abstract class Bird implements Cloneable { protected String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public abstract Bird cloneObject() throws CloneNotSupportedException; }Parrot.java
public class Parrot extends Bird { public Parrot() { name = PrototypeFactory.PARROT; } @Override public Bird cloneObject() throws CloneNotSupportedException { System.out.println("Cloning a Parrot object"); return (Bird) super.clone(); } }Eagle.java
public class Eagle extends Bird { public Eagle() { name = PrototypeFactory.EAGLE; } @Override public Bird cloneObject() throws CloneNotSupportedException { System.out.println("Cloning an Eagle object"); return (Bird) super.clone(); } }Sparrow.java
public class Sparrow extends Bird { public Sparrow() { name = PrototypeFactory.SPARROW; } @Override public Bird cloneObject() throws CloneNotSupportedException { System.out.println("Cloning a Sparrow object"); return (Bird) super.clone(); } }
PrototypeFactory class will create prototype instances of Parrot, Sparrow and Eagle stores then in a HashMap. The getBirdInstance method first get the prototype instance of requested bird object from HashMap and the returns a copy of it.
import java.util.Map; import java.util.HashMap; public class PrototypeFactory { public static final String PARROT = "Parrot"; public static final String SPARROW = "Sparrow"; public static final String EAGLE = "Eagle"; private Map<String, Bird> prototypeList = new HashMap<String, Bird>(); public void initialize() { prototypeList.put(PrototypeFactory.PARROT, new Parrot()); prototypeList.put(PrototypeFactory.SPARROW, new Sparrow()); prototypeList.put(PrototypeFactory.EAGLE, new Eagle()); } public Bird getBirdInstance(String name) throws CloneNotSupportedException { return (Bird)prototypeList.get(name).cloneObject(); } }
PrototypePatternExample class uses PrototypeFactory object to create object of Parrot, Sparrow and Eagle classes.
public class PrototypePatternExample { public static void main(String[] args) { PrototypeFactory factory = new PrototypeFactory(); factory.initialize(); try { factory.getBirdInstance(PrototypeFactory.PARROT); factory.getBirdInstance(PrototypeFactory.SPARROW); factory.getBirdInstance(PrototypeFactory.EAGLE); } catch (CloneNotSupportedException e) { e.printStackTrace(); } } }
Output
Cloning a Parrot object Cloning a Sparrow object Cloning an Eagle object
Best Practices of Prototype Pattern
- Deep Copy Considerations :Think about whether a shallow or deep copy is better when using the clone method. While deep copies additionally copy the object's internal references, shallow copies merely copy the item itself. Select the strategy that best meets the needs of your application.
- Implement Cloneable Interface : Make sure your prototype classes implement the Cloneable interface by making sure they implement the Cloneable interface. Because of this, the clone method can be used without raising the CloneNotSupportedException exception.
- Immutable Objects : Cloning gets easier if your objects are immutable because you just need to clone the object itself. More dependable and efficient behavior may result from this.
- Copy Constructor as an Alternative : You might want to provide a copy constructor in place of or in addition to the clone method. This provides a another method of copying things and allows for more precise control over what is copied.
- Use with Factories :To control the instantiation of prototype objects, combine the Prototype pattern with creational patterns such as the Factory Method or Abstract Factory. This can improve your object creation process's configurability and flexibility.
- Ensure Cloning Completeness : Verify that every part of an object, including its internal state and any nested objects, has been cloned. In order to prevent unexpected behavior when utilizing the cloned objects, this completeness is essential.
- Avoid Cloning Complex Objects : Give cloning serious thought if your objects have intricate connections or data structures. Other methods, like as serialization or manual copying, might be more suitable in some circumstances.
- Handle Circular References : Pay attention to how your objects are treated during the cloning process if they include circular references (such as references back to themselves). In order to prevent unexpected behavior or infinite loops, you might need to handle these references with caution.
Factory Design Pattern |
Builder Design Pattern |
Bridge Design Pattern |
Composite Design Pattern |
Decorator Design Pattern |
List of Design Patterns |