tl  tr
  Home | Tutorials | Articles | Videos | Products | Tools | Search
Interviews | Open Source | Tag Cloud | Follow Us | Bookmark | Contact   
 Design Patterns > Java Design Patterns > Prototype

Prototype 

Prototype comes under creational design pattern. It simplifies the object creation by cloning from existing instance. Making an object copy from existing instance saves both time and memory. In Java we know Strings are immutable and as cloned objects share the same string references, it provides a sort of memory optimization.

Behaviour & Advantages

  • It avoids the cost of creating new instance.
  • Creates a new object by copying its properties.

This example shows cloning an Employee object. For that we need to implement java.lang.Cloneable interface. Class java.lang.Object has a native implementation of clone() method which returns a Shallow copy of the current object. Invoking clone() method on object without implementing java.lang.Cloneable interface results in java.lang.CloneNotSupportedException exception.

File Name  :  
com/bethecoder/tutorials/dp/prototype/Employee.java 
   
package com.bethecoder.tutorials.dp.prototype;

import java.util.Arrays;

public class Employee implements Cloneable {

  private String name;
  private String designation;
  private int salary;
  private int [] luckyNums;
  
  public Employee(String name, String designation, int salary) {
    super();
    this.name = name;
    this.designation = designation;
    this.salary = salary;
  }

  public Employee clone() throws CloneNotSupportedException {
    try {
      return (Employeesuper.clone();
    catch (CloneNotSupportedException e) {
      System.out.println(e);
      throw e;
    }
  }

  public String toString() {
    StringBuilder sb = new StringBuilder("[");
    sb.append("name = ").append(name).append(", ");
    sb.append("designation = ").append(designation).append(", ");
    sb.append("salary = ").append(salary).append(", ");
    sb.append("lucky nums = ").append(Arrays.toString(luckyNums));
    sb.append("]").append(this.getClass().getSimpleName());
    return sb.append("@").append(this.hashCode()).toString();
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getDesignation() {
    return designation;
  }

  public void setDesignation(String designation) {
    this.designation = designation;
  }

  public int getSalary() {
    return salary;
  }

  public void setSalary(int salary) {
    this.salary = salary;
  }

  public int[] getLuckyNums() {
    return luckyNums;
  }

  public void setLuckyNums(int[] luckyNums) {
    this.luckyNums = luckyNums;
  }

}
   

In Shallow copy primitive values are copied by value and objects by reference. In Java Arrays (both primitive & object) are considered as objects and hence copied by reference. So changes to the array in original object reflects in the cloned object or vice versa. We can observe that primitive value (salary) change in cloned object didn't effect the same in original object. However the changes we have made for object (luckyNums) effect the original object.

File Name  :  
com/bethecoder/tutorials/dp/prototype/Test.java 
   
package com.bethecoder.tutorials.dp.prototype;

public class Test {

  /**
   @param args
   @throws CloneNotSupportedException 
   */
  public static void main(String[] argsthrows CloneNotSupportedException {

    Employee emp = new Employee("Sri Ram""JL"56000);
    emp.setLuckyNums(new int [] { 13});
    System.out.println(emp);
    
    Employee emp2 = (Employeeemp.clone();
    System.out.println(emp2);
    
    //After change
    emp2.setDesignation("SL");
    emp2.setSalary(86000);
    emp2.getLuckyNums()[0100;
    
    System.out.println();
    System.out.println(emp);
    System.out.println(emp2);
  }

}
   

It gives the following output,
[name = Sri Ram, designation = JL, salary = 56000, 
	lucky nums = [1, 3, 5]]Employee@19770577
[name = Sri Ram, designation = JL, salary = 56000, 
	lucky nums = [1, 3, 5]]Employee@28117098

[name = Sri Ram, designation = JL, salary = 56000, 
	lucky nums = [100, 3, 5]]Employee@19770577
[name = Sri Ram, designation = SL, salary = 86000, 
	lucky nums = [100, 3, 5]]Employee@28117098

To avoid these pitfalls we can go with Deep Copy of objects. A new example with Copy Constructor and Deep Copy strategy is shown below,

File Name  :  
com/bethecoder/tutorials/dp/prototype/Employee2.java 
   
package com.bethecoder.tutorials.dp.prototype;

import java.util.Arrays;

public class Employee2 {

  private String name;
  private String designation;
  private int salary;
  private int [] luckyNums;
  
  public Employee2(String name, String designation, int salary) {
    super();
    this.name = name;
    this.designation = designation;
    this.salary = salary;
  }

  /**
   * Copy constructor.
   
   @param name
   @param designation
   @param salary
   */
  public Employee2(Employee2 other) {
    this.name = other.name;
    this.designation = other.designation;
    this.salary = other.salary;
    
    if (other.luckyNums != null) {
      this.luckyNums = new int[other.luckyNums.length];
      for (int i = ; i < other.luckyNums.length ; i ++) {
        this.luckyNums[i= other.luckyNums[i];
      }
    }
  }
  
  public Employee2 clone() {
    return new Employee2(this);
  }

  public String toString() {
    StringBuilder sb = new StringBuilder("[");
    sb.append("name = ").append(name).append(", ");
    sb.append("designation = ").append(designation).append(", ");
    sb.append("salary = ").append(salary).append(", ");
    sb.append("lucky nums = ").append(Arrays.toString(luckyNums));
    sb.append("]").append(this.getClass().getSimpleName());
    return sb.append("@").append(this.hashCode()).toString();
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getDesignation() {
    return designation;
  }

  public void setDesignation(String designation) {
    this.designation = designation;
  }

  public int getSalary() {
    return salary;
  }

  public void setSalary(int salary) {
    this.salary = salary;
  }

  public int[] getLuckyNums() {
    return luckyNums;
  }

  public void setLuckyNums(int[] luckyNums) {
    this.luckyNums = luckyNums;
  }

}
   

Here we can observe that cloned object and orginal object are independent and changes to one doesn't effect the other.

File Name  :  
com/bethecoder/tutorials/dp/prototype/Test2.java 
   
package com.bethecoder.tutorials.dp.prototype;

public class Test2 {

  /**
   @param args
   */
  public static void main(String[] args) {

    Employee2 emp = new Employee2("Sri Ram""JL"56000);
    emp.setLuckyNums(new int [] { 13});
    System.out.println(emp);
    
    Employee2 emp2 = (Employee2emp.clone();
    System.out.println(emp2);
    
    //After change
    emp2.setDesignation("SL");
    emp2.setSalary(86000);
    emp2.getLuckyNums()[0100;
    
    System.out.println();
    System.out.println(emp);
    System.out.println(emp2);
  }

}
   

It gives the following output,
[name = Sri Ram, designation = JL, salary = 56000, 
	lucky nums = [1, 3, 5]]Employee2@14978587
[name = Sri Ram, designation = JL, salary = 56000, 
	lucky nums = [1, 3, 5]]Employee2@19770577

[name = Sri Ram, designation = JL, salary = 56000, 
	lucky nums = [1, 3, 5]]Employee2@14978587
[name = Sri Ram, designation = SL, salary = 86000, 
	lucky nums = [100, 3, 5]]Employee2@19770577




 
  


  
bl  br