Monday, 28 January 2008

Prototype Pattern in Java

Today we will discuss another GoF Creational Pattern "Prototype".

Overview:
Make new objects by cloning the objects which you set as prototypes.Use Prototype Pattern when creating an instance of a given class is either expensive or complicated.A key aspect to this pattern is that client code can make new instances without knowing which specific class is being instantiated.

Intent:
Specify the kinds of objects to create using a prototypical instance,and create new objects by copying this prototype.

Definition:
Specify the kinds of objects to create using a prototypical instance,and create new objects by cloning this prototype.Prototype pattern is used when creating an instance of a class is very-time consuming or complex in some way.Then,rather than creating more instances,you make copies of the original instance,modifying them as appropriate.
Prototypes can also be used whenever we need classes that differ only in the type of processing they offer,for example in parsing of strings representating numbers in diff radixes.In this sense prototype is nearly the same as Examplar pattern described in Coplien[1992].

Let's consider the case of an extensive database where we need to make a number of queries to construct an answer.Once we have this answer as a table or ResultSet,we might like to manipulate it to produce other answers without issuing additional queries.

Participants:
-> Prototype: Declares an interface for cloning itself.
-> Concrete Prototype: Implements an operation for cloning itself.
-> Client: Creates a new object by asking a prototype to clone itself.

"Copy Constructor" is one form of Prototype pattern.

We would lose the advantage of Polymorphism that the GoF formulation of the Prototype pattern gives you. - NatPryce.

Prototype means making a clone.This implies cloning of an object to avoid creation.If the cost of creating a new object is large and creation is resource intensive,we clone the object.We use the interface Cloneable and call its method clone() to clone the object.

One thing we cannot use the clone as it is.We need to instantiate the clone before using it.This can be a performance drawback.This also gives sufficient access to the data and methods of the class.This means that data access methods have to be added to the protoype once it has been cloned.

Alternative To the Flyweight pattern is prototype pattern which allows polymorphic copies of existing objects.The object clone() method signature provides support for Prototype pattern.

Prototypes are useful when object initialization is expensive,and you anticipate few variations on the initialization parameters.Then we could keep already-initialized objects in a table,and clone an exisiting object instead of expensively creating a new one from scratch.

Immutable objects can be returned directly when using Prototyping,avoiding the copying overhead.

Recall,that the idea of prototype is that we are passed an object and use that object as a template to create a new object and use that object as a template to create a new object.Because we might not know the implementation details of the object,we cannot create a new instance of the object and copy all of its data.(Some of the data may not be accessible via methods.) So we ask the object itself to give a copy of itself.

Java provides a simple interface named Cloneable that provides an implementation of the Prototype pattern.If we have an object that is Cloneable,we can call its clone() method to create a new Instance of the object with the same values.

Note that,Cloneable is a marker interface.It merely acts as a tag to state that we really want instance of the class to be cloned.If we donot implement Cloneable,the super.clone() method will throw CloneNotSupportedException.

The object implementation of clone() performs a shallow copy of the object in question.That is,it copies the values of the fields in the object,but not any actual objects that may be pointed to.In other words,the new object will point to the same objects the old object pointed to.

clone() method always retuns an object of type Object.we must cast it to the actual type of the object we are cloning.There are 3 other significant restrictions on the clone method.

-> It is a protected method and can only be called from within the same class or the module that contains the class.
-> We can only clone objects which are declared to implement the Cloneable interface.
-> Objects that cannot be cloned throw the CloneNotSupportedException.

Please remember clone() method is a shallow copy of the original class.In other words,references of the data objects are copies,but they refer to the same underlying data.Thus any operation we perform on the copied data will also occur on the original data in the Prototype class.

In some cases,this shallow copy is acceptable,but if you want to make a deep copy of the data ,there is a clever trick using the serializable interface.A class is said to be serializable,if we can write it out as a stream of bytes and read those bytes back in to reconstruct the class.This is how RMI is implemented.

Benefits:-> Adding and removing products at runtime.
-> Specifying new objects by varying values.
-> Specifying new objects by varying structure.
-> Reduced subclassing.
-> Configure an application with classes dynamically.
-> Hides the complexities of making new instances from the client.
-> Provides the option for the client to generate objects whose type is unknown.
-> In some circumstances,copying an object is more efficient than creating a new object.

Usage:-> When the classes to instantiate are specified at runtime.
-> When instances of a class can have of only a few combinations of state.

Consequences of Prototype Pattern:

-> Classes that have circular references to other classes cannot really be cloned.
-> One Difficulty in implementing the Prototype Pattern in Java is that if the classes already exist,we may not be able to change them to add the required clone or deepClone methods.The deepClone() method can be difficult if all the class objects contained in the class cannot be declared to implement Serializable.
-> Finally idea of having prototype classes to copy implies that we have sufficient access to the data or methods to these prototype classes so that we can modify the data once we have cloned the class.

Below example describes one usage of Prototype Pattern.


package patterns;

import java.io.Serializable;

class Party implements Serializable,Cloneable {

public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}


class Person extends Party {

private Long personId;
private PersonPersonalDetails ppd;

public Long getPersonId() {
return personId;
}

public void setPersonId(Long personId) {
this.personId = personId;
}

public PersonPersonalDetails getPpd() {
return ppd;
}

public void setPpd(PersonPersonalDetails ppd) {
this.ppd = ppd;
}

public Object clone() throws CloneNotSupportedException {
Person person=(Person)super.clone();
person.setPpd((PersonPersonalDetails)this.getPpd().clone());
return person;
}
}


class PersonPersonalDetails implements Serializable,Cloneable {

private String firstName;
private String lastName;

public String getFirstName() {
return firstName;
}

public void setFirstName(String firstName) {
this.firstName = firstName;
}

public String getLastName() {
return lastName;
}

public void setLastName(String lastName) {
this.lastName = lastName;
}

public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}


public class PrototypePatternDemo {

public static void main(String[] args) {

Person p=new Person();
p.setPersonId(new Long(20));
PersonPersonalDetails ppd=new PersonPersonalDetails();
ppd.setFirstName("Prashant");
ppd.setLastName("Jalasutram");
p.setPpd(ppd);

try {
Person personClone=(Person)p.clone();
// Updating Details of Clone and Prining Master and Clone.
//Please note that master will not get effected at all when clone get modified.
personClone.setPersonId(new Long(100));
System.out.println("Cloned Person Id:" + personClone.getPersonId());
System.out.println("Master Person Id:" + p.getPersonId());
}catch(Exception ex) { ex.printStackTrace();}

}
}



Hope this explanation helps.

No comments: