Intent

"Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this instance"

Motivation

The prototype pattern is a creational design pattern and it’s primarily used to create new objects (instances) by cloning other objects. Creating certain objects at runtime can be a computationally expensive affair and the Prototype pattern looks to help improve upon this by essentially allowing the program to copy and paste an existing object. By doing this we save time and resources and can then just modify our newly pasted object to suit our needs.

UML Diagram

Prototype design pattern tutorial

Pros and Cons of the Prototype Design Pattern

Pros: Using the prototype design pattern allows creation and deletion of classes at runtime. Reduced subclassing Configure an application with classes dynamically Hides the complexities of making new instances from the client. In certain circumstances, copy an object is more efficient than creating a new object Using a Prototype design pattern hides concrete product classes from the client and forces you to program to an Interface. Cons: Classes that have circular references can be difficult to deep clone.

Programming the Prototype Design Pattern

In this example we are going to be building up a demo which utilizes a PrototypeFactory due to this being typical of most Prototype implementations.

Character Interface:

public interface Character extends Cloneable{

	public Character makeCopy();

}

Character Factory

public class CharacterFactory {

	public Character getClone(Character enemySample){
		return enemySample.makeCopy();
	}

}

Enemy Class:

public class Enemy implements Character{

	public Enemy(){
		System.out.println("Enemy Character deployed!");
	}

        // this implements our copy function as defined in our
        // Character interface. This is an essential part of the
        // Prototype pattern implementation as it allows us to perform
        // the copying of already instantiated objects and thus save
        // time and resources.
	@Override
	public Character makeCopy() {
		// TODO Auto-generated method stub
		System.out.println("Enemy has spawned");

		Enemy enemyObject = null;

		try {
			enemyObject = (Enemy) super.clone();
		} catch (CloneNotSupportedException e){
			System.err.println(e);
		}

		return enemyObject;
	}

	public String toString(){
		return "Enemy Deployed";
	}
}

Ally Class:

public class Ally implements Character{

	public Ally(){
		System.out.println("Ally Character deployed!");
	}

	@Override
	public Character makeCopy() {
		// TODO Auto-generated method stub
		System.out.println("Ally has spawned");

		Ally enemyObject = null;

		try {
			enemyObject = (Ally) super.clone();
		} catch (CloneNotSupportedException e){
			System.err.println(e);
		}

		return enemyObject;
	}

	public String toString(){
		return "Ally Deployed";
	}
}

Main Class

public class Main {

	public static void main(String args[]){
		CharacterFactory enemyFactory = new CharacterFactory();

                // here we instantiate the objects that we are going to need
                // to clone at runtime as it's far less computationally expensive
                // to essentially copy and paste an object at run time than create
                // a whole new object.
		Enemy mainEnemy = new Enemy();
		Ally mainAlly = new Ally();

		Enemy clonedEnemy = (Enemy) enemyFactory.getClone(mainEnemy);
		Enemy clonedEnemy2 = (Enemy) enemyFactory.getClone(mainEnemy);
		Enemy clonedEnemy3 = (Enemy) enemyFactory.getClone(mainEnemy);
		Ally clonedAlly = (Ally) enemyFactory.getClone(mainAlly);
		Ally clonedAlly2 = (Ally) enemyFactory.getClone(mainAlly);

                // We print out the objects identityHashCodes to show that the cloned objects
                // are completely different from our initial instantiated objects.
		System.out.println(System.identityHashCode(System.identityHashCode(mainEnemy)));
		System.out.println(System.identityHashCode(System.identityHashCode(clonedEnemy)));
	}

}