Reading Time: 5 minutes

Prototype Design Pattern Java Real World Example

Prototype Design Pattern

Prototype Design Pattern is one of the classic Gang of Four Creational Design pattern. Prototype Design pattern is used when the Object creation is a costly affair and requires a lot of time and resources and you have a similar object already existing.

Prototype Design Pattern is a creational design pattern that allows cloning objects, even complex ones, without coupling to their specific classes. This pattern provides a mechanism to copy the original object to a new object and then modify it according to our needs. This pattern uses java cloning to copy the object.

This tutorial is a part of Creational Design Pattern Series. Take a look at other Creational Design Patterns:

  1. Singleton Creational Design Pattern Java Explained [6 Code Example]
  2. Factory Design Pattern Java Simplified [Simple Real World Example]
  3. Factory Method Design Pattern Java Simple Detailed Examples
  4. Abstract Factory Design Pattern Java Real World Example
  5. Builder Design Pattern Java Real World Example
  6. Prototype Design Pattern Java Real World Example

 

The Class must implement a marker interface “Cloneable”. This will allow objects of this class to be cloned.

By Default, Java implements shallow cloning of the object. clone() needs to be overridden if one wants to implement deep cloning.

Shallow Cloning

The shallow copy of an object will have the exact copy of all the fields of original object. If original object has any references to other objects as fields, then only references of those objects are copied into clone object, copy of those objects are not created. That means any changes made to those objects through clone object will be reflected in original object or vice-versa.

Deep Cloning

Deep copy of an object will have exact copy of all the fields of original object just like shallow copy. But in additional, if original object has any references to other objects as fields, then copy of those objects are also created by calling clone() method on them or by creating new object and setting the original values in it. That means clone object and original object will be 100% disjoint. They will be 100% independent of each other.

 

When to use the Prototype Design Pattern?

  1. Creating an object is an expensive cost operation and it would be more efficient to copy an object.
  2. You need to hide the complexity of creating new instance from the client.
  3. Objects are required that are similar to existing objects and has mostly immutable fields.

 

Prototype Design Pattern Implementation

  1. Create a Product class which implements cloneable interface. List all the parameters and create setter getter for them. Create appropriate constructors.
  2. If parameters contain all immutable fields, there is no need to override clone() method.
  3. If any parameter is mutable, We need to override clone() method and write appropriate deep cloning logic.
  4. The clone method will return a new object of the Product class with all field values as per original product object.

Prototype Design Pattern Real World Example

  1. Let’s consider a scenario where the client is running a Movie Rating website. Client is using a External Rest API to fetch all the details(name,releasedate,genre etc) related to the movie title. As this is an external API, Client have to pay certain fee for every API call.
  2. Client website is designed in such a way that rating of the Movie can change periodically, but other data i.e Name, Genre and releaseDate is constant and will never change.
  3. Client needs an new object of Movie for every visitor who rates the movie.
  4. Inorder to save money, The External API should be hit only once and for every subsequent request, We can use prototype design pattern to return the cloned object of the previous object.

Product

MovieDataBaseDeep will act as product class which will implement cloneable interface.

package com.adevguide.java.designpatterns.prototype;

import java.util.ArrayList;
import java.util.List;

public class MovieDataBaseDeep implements Cloneable {

    private String movieName;
    private String releaseDate;
    private List<String> genre;
    private List<String> ratings;

    public MovieDataBaseDeep() {
        System.out.println("Defaut Constructor");
    }

    public MovieDataBaseDeep(String movieName, String releaseDate, List<String> genre, List<String> ratings) {
        this.movieName = movieName;
        this.releaseDate = releaseDate;
        this.genre = genre;
        this.ratings = ratings;
    }

    public void getData() {
        System.out.println("Getting Data from External REST API");
        this.movieName = "The Dark Knight";
        this.releaseDate = "2018";
        this.genre = new ArrayList<String>();
        this.genre.add("Drama");
        this.genre.add("Thriller");
        this.ratings = new ArrayList<String>();
        this.ratings.add("IMDB:9");
        this.ratings.add("RottenTomatoes:94%");
        this.ratings.add("MetaCritic:84%");
        System.out.println("You have been charged 1$ for last API call.");
    }

    @Override
    public String toString() {
        return String.format("MovieDataBaseDeep [movieName=%s, releaseDate=%s,\n genre=%s, ratings=%s]", movieName,
                releaseDate, genre, ratings);
    }

    @Override
    protected MovieDataBaseDeep clone() throws CloneNotSupportedException {
        List<String> deepRatings = new ArrayList<String>();
        deepRatings.addAll(this.ratings);//deep cloning of ratings as it is mutable
        //shallow cloning of genre

        return new MovieDataBaseDeep(this.movieName, this.releaseDate, this.genre, deepRatings);
    }

    public String getMovieName() {
        return movieName;
    }

    public void setMovieName(String movieName) {
        this.movieName = movieName;
    }

    public String getReleaseDate() {
        return releaseDate;
    }

    public void setReleaseDate(String releaseDate) {
        this.releaseDate = releaseDate;
    }

    public List<String> getGenre() {
        return genre;
    }

    public void setGenre(List<String> genre) {
        this.genre = genre;
    }

    public List<String> getRatings() {
        return ratings;
    }

    public void setRatings(List<String> ratings) {
        this.ratings = ratings;
    }

}
  1. MovieDataBaseDeep() is a default onstructor which will be called while creation of original object.
  2. MovieDataBaseDeep(String movieName, String releaseDate, List<String> genre, List<String> ratings) is paramaterized construction which clone method will use to create cloned object.
  3. getData() is a dummy External API call which will populate the original object with movie data.
  4. clone() is an overridden method. This is performing deep cloning of ratings and shallow cloning of rest of the fields.

Client

Client class will act as out client which will perform the cloning of objects.

package com.adevguide.java.designpatterns.prototype;

public class Client {

    public static void main(String[] args) {

        try {
            MovieDataBaseDeep originalObject = new MovieDataBaseDeep(); // Default Constructor call
            originalObject.getData(); // External API call
            System.out.println("originalObject: " + originalObject);
            System.out.println("*******************************************************");
            MovieDataBaseDeep clonedObject = originalObject.clone(); // Object creation using Cloning
            clonedObject.getRatings().remove(2); // change in mutable rating field which is deep cloned
            clonedObject.getGenre().add("SuperHero"); // change in genre which is shallow cloned
            System.out.println("clonedObject: " + clonedObject); // clonedObject shows all above changes
            System.out.println("*******************************************************");
            System.out.println("originalObject: " + originalObject); // Original Object shows changes in    //only genre as it was shallow cloned. Changes in rating will not occur in original onject as it is deep //cloned.
            System.out.println("*******************************************************");

        } catch (CloneNotSupportedException e) {

            e.printStackTrace();
        }

    }

}

Output:

Defaut Constructor
Getting Data from External REST API
You have been charged 1$ for last API call.
originalObject: MovieDataBaseDeep [movieName=The Dark Knight, releaseDate=2018,
genre=[Drama, Thriller], ratings=[IMDB:9, RottenTomatoes:94%, MetaCritic:84%]] *******************************************************
clonedObject: MovieDataBaseDeep [movieName=The Dark Knight, releaseDate=2018,
genre=[Drama, Thriller, SuperHero], ratings=[IMDB:9, RottenTomatoes:94%]] *******************************************************
originalObject: MovieDataBaseDeep [movieName=The Dark Knight, releaseDate=2018,
genre=[Drama, Thriller, SuperHero], ratings=[IMDB:9, RottenTomatoes:94%, MetaCritic:84%]] *******************************************************

 

ratings has been deep cloned, that’s why any change in cloned object’s rating will not impact Original Object.

genre is supposed to be permanent for a movie and should not change. That’s why it has been shallow cloned. In a case where genre needs to be change, It should be changed universally that is in Original Object too. Shallow cloning will do just that.

 

Advantages of Prototype Design Pattern

  1. Reduces Object creation cost. Also crutial when new object creation is not possible (Provided to code).
  2. Reduces load on Database if same object needs to be queried multiple time.
  3. Save total operating cost my minimizing costly external API calls.
  4. Allows to easily modify the existing class and its prototyping function which would not have been the case if cloning takes place elsewhere.

Pitfalls of Prototype Design Pattern

  • Each subclass of Prototype must implement the clone() operation which may be difficult, when the classes under consideration already exist.
  • When large number of mutable objects are present, Cloning becomes complicated.
  • Implementing clone() can be difficult when their internals include objects that don’t support copying or have circular references.

Example of Prototype Design Pattern

There aren’t any actual implemenation of the Prototype Design Pattern that can be seen in JDK, but Object.clone() is an good candidate which follows the design.

 

Prototype Design Pattern Source Code

Entire source code of this tutorial can be found in our GitHub Repository.

GitHub Source Code

References

https://dzone.com/articles/design-patterns-prototype