YouTip LogoYouTip

Prototype Pattern

# Prototype Pattern The Prototype Pattern is used to create duplicate objects while ensuring performance. This type of design pattern is a creational pattern and provides one of the best ways to create an object. This pattern involves implementing a prototype interface which tells to create a clone of the current object. This pattern is used when creation of an object directly is costly. For example, an object is to be created after a costly database operation. We can cache the object, returns the clone on next request and update the database as and when needed thus reducing database calls. ## Summary ### Prototype Pattern **Intent**: Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype. **Main Solution**: To dynamically add and remove prototypes at runtime. ### When to use * When a system should be independent of how its products are created, composed, and represented. * When the class to instantiate is specified at runtime, for example, by dynamic loading. * To avoid building a class hierarchy of factories that parallels the class hierarchy of products. * When instances of a class can have one of only a few different combinations of state. It may be more convenient to clone a prototype and change the state than to instantiate a new class. ### How to solve Clone the existing object using a fast and efficient mechanism (e.g., binary cloning). ### Key Code * Implement the clone operation: * In Java, implement the `Cloneable` interface and override the `clone()` method. * In .NET, use the `MemberwiseClone()` method of the `Object` class for a shallow copy, or use serialization for a deep copy. * Isolate the client code from the concrete types of objects it needs to create, requiring the "volatile class" to have a stable interface. ### Application Examples * Cell division. * The `Object.clone()` method in Java. ### Advantages * Performance improvement. * Avoids the constraints of constructors. ### Disadvantages * Implementing the clone method can be complex, especially if the class contains circular references or objects that do not support serialization. * The `Cloneable` interface must be implemented. ### Use Cases * Resource optimization. * When class initialization consumes a lot of resources (e.g., data, hardware resources). * Scenarios requiring high performance and security. * When creating an object via `new` requires complex data preparation or access permissions. * When an object needs multiple modifiers. * When an object needs to be provided to other objects and might be modified by various callers. * Often used with the Factory Method pattern, creating objects via `clone` and then providing them to callers via the factory method. ### Notes Unlike directly instantiating a class to create new objects, the Prototype pattern generates new objects by copying existing objects. A shallow copy is achieved by implementing `Cloneable`, while a deep copy is achieved by implementing `Serializable` and reading the binary stream. ### Structure The Prototype pattern includes the following main roles: * **Prototype Interface**: Defines an interface for cloning itself, typically including a `clone()` method. * **Concrete Prototype**: Implements the Prototype interface and is responsible for the actual cloning operation. This class needs to implement the `clone()` method, usually using shallow or deep copy to duplicate itself. * **Client**: Uses a prototype instance to create new objects. The client calls the `clone()` method of the prototype object to create new objects, rather than using the constructor directly. ## Implementation We are going to create an abstract class _Shape_ and concrete classes extending the _Shape_ class. Next step is to define the class _ShapeCache_, which stores shape objects in a _Hashtable_ and returns their clone when requested. The _PrototypePatternDemo_ class will use _ShapeCache_ class to get a _Shape_ object. ![Image 2: UML Diagram for Prototype Pattern](#) ### Step 1 Create an abstract class implementing the _Cloneable_ interface. ## Shape.java ```java public abstract class Shape implements Cloneable{ private String id; protected String type; abstract void draw(); public String getType(){ return type; } public String getId(){ return id; } public void setId(String id){ this.id = id; } public Object clone(){ Object clone = null; try{ clone = super.clone(); }catch(CloneNotSupportedException e){ e.printStackTrace(); } return clone; } } ### Step 2 Create concrete classes extending the above class. ## Rectangle.java ```java public class Rectangle extends Shape{ public Rectangle(){ type = "Rectangle"; } @Override public void draw(){ System.out.println("Inside Rectangle::draw() method."); } } ## Square.java ```java public class Square extends Shape{ public Square(){ type = "Square"; } @Override public void draw(){ System.out.println("Inside Square::draw() method."); } } ## Circle.java ```java public class Circle extends Shape{ public Circle(){ type = "Circle"; } @Override public void draw(){ System.out.println("Inside Circle::draw() method."); } } ### Step 3 Create a class to get concrete classes from database and store them in a _Hashtable_. ## ShapeCache.java ```java import java.util.Hashtable; public class ShapeCache{ private static HashtableshapeMap = new Hashtable(); public static Shape getShape(String shapeId){ Shape cachedShape = shapeMap.get(shapeId); return(Shape)cachedShape.clone(); } // For each shape run database query and create shape // shapeMap.put(shapeKey, shape); // For example, we are adding three shapes public static void loadCache(){ Circle circle = new Circle(); circle.setId("1"); shapeMap.put(circle.getId(),circle); Square square = new Square(); square.setId("2"); shapeMap.put(square.getId(),square); Rectangle rectangle = new Rectangle(); rectangle.setId("3"); shapeMap.put(rectangle.getId(),rectangle); } } ### Step 4 _PrototypePatternDemo_ uses _ShapeCache_ class to get clones of shapes stored in the _Hashtable_. ## PrototypePatternDemo.java ```java public class PrototypePatternDemo{ public static void main(String[]args){ ShapeCache.loadCache(); Shape clonedShape = (Shape)ShapeCache.getShape("1"); System.out.println("Shape : " + clonedShape.getType()); Shape clonedShape2 = (Shape)ShapeCache.getShape("2"); System.out.println("Shape : " + clonedShape2.getType()); Shape clonedShape3 = (Shape)ShapeCache.getShape("3"); System.out.println("Shape : " + clonedShape3.getType()); } } ### Step 5 Verify the output. Shape : Circle Shape : Square Shape : Rectangle
← Adapter PatternBuilder Pattern β†’