Prototype Pattern

Whenever object instantiation implies a high computational cost or an uncertain availability of data, we should consider the possibility of cloning, as it may be cheaper than creating brand new instances.

Let us imagine an object that, while being initialized, calls a remote server to get some data for some of it’s attributes. Let’s also imagine that particular data changes only once every day at exactly midnight. Finally let’s pretend our system, needs to create one of those objects for each user and has an average of five hundred users per minute.

When we imagine a situation as described, some challenges immediately come up.

  1. We will need to make a call to a remote server around five hundred times per minute, which will increase the traffic on our network and put some strain in the database.
  2. What if the server on the other side does not respond ?

In a situation like this, instantiating objects can either become expensive (1), or become impossible, if the data source becomes unavailable (2).

This is where the prototype pattern comes in. We instantiate one object, store that object, and re-use it by making copies of it, keeping the original untouched.

Java actually made it very simple by providing us the Cloneable interface.

Is that all ?

Yes and No. When our object contains other objects inside, creating a copy of it has a pitfall we need to account for: we may need to clone the object and also all the objects inside. This introduces us to the concepts of shallow copy and deep copy and depending on our objective, we need to choose which method we want to implement.

Shallow copy

Shallow copy is the standard way in Java when we use the Cloneable interface. In a shallow copy we will receive a new object which is a precise copy of the original. This means that if an object has other objects inside, when we change one of the inner objects all copies (clones) will be affected.

in other words, the cloned object is a new object, but inner objects of the copy are exactly the same as in the original.

Deep copy

Deep copy method will provide us with a new object(the clone), maintaining the structure of the original object replicating it, but all inner objects will also be new. In this case, all inner objects will be independent, so any changes on them will only affect the particular object being altered and not all of them.

Obviously that deep copy involves a higher cost, as it has to instantiate all the inner objects of the original as well as copy their exact states, so depending on what we want to achieve we should dedicate some time to examine the situation.

In this article we will use an example that relies on primitives, so shallow copy will work perfectly.

The example

We will be creating a board game. For our board game we have three heroes (an Elf, a Wizard, and a Goblin). We will pretend that to instantiate our heroes, we need to read some values from a database as health, magic, attack and defense values. If our board is initialized with 10 of each hero, we would need to fetch values from the database several times…not a good idea…So we clone.

A simple UML representation of the prototype pattern for our case could be

In the above representation, Hero is the interface that will act as an contract for all heroes. Goblin, Elf and Wizard are the classes that define them, and we have a HeroWareHouse, that will be used to store one hero of each. Those will be our “master” copy for each one (remember that we are pretending that for each of them we are reading values from a database)

We will be using an UUID object to identify each hero. Some attention will be needed on that attribute, as each hero, will need to have a different UUID.

We will be using a support enumerator, which will act as the database for the heroes properties.

HeroProperties.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package Prototype.Shallow;

public enum HeroProperties {

ELF_ATTACK(60),
ELF_DEFENSE(80),
ELF_MAGIC(32),
GOBLIN_ATTACK(75),
GOBLIN_DEFENSE(80),
GOBLIN_MAGIC(32),
WIZARD_ATTACK(75),
WIZARD_DEFENSE(80),
WIZARD_MAGIC(32);

private final int value;

HeroProperties(int value) {
this.value = value;
}

public int getValue() {
return value;
}
}

The Hero Interface, will extend Cloneable from the JAVA API. We could actually implement the Cloneable interface directly in each of the heroes classes but would make our code more complicated as having a common type to handle our Heroes simplifies our work.

Hero.java
1
2
3
4
5
6
7
8
9
package Prototype.Shallow;

import java.util.UUID;

public interface Hero extends Cloneable{

void setID(UUID heroID);
Hero clone() throws CloneNotSupportedException;
}

Now we have the three Hero classes. In our little example they are very similar. For simplicity we harcoded the health value as 100 for all of them.

In the Hero classes we override the clone method, and please do note, that due the implementation we’ve used, we do need to cast the cloned object to the proper type before returning it.
In the event that our object can’t be cloned, we will return a null object. Although returning nulls is never a good solution, and could be swapped by the Java Optional, we will leave it as is for the sake of simplicity, and we do not even check for nulls later…(I, know..don’t crucify me)

Elf.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
package Prototype.Shallow;

import java.util.UUID;

public class Elf implements Hero {

UUID heroID;
int attack;
int defense;
int magic;
int health;

public Elf() {
this.attack = HeroProperties.ELF_ATTACK.getValue();
this.defense = HeroProperties.ELF_DEFENSE.getValue();
this.magic = HeroProperties.ELF_MAGIC.getValue();
this.health = 100;
}

@Override
public void setID(UUID heroID) {
this.heroID = heroID;
}
@Override
public Elf clone() throws CloneNotSupportedException {
try {
return (Elf) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
@Override
public String toString() {

return "Elf{" +
"attack=" + attack +
", defense=" + defense +
", magic=" + magic +
", health=" + health +
'}';
}
}

The Goblin class definition

Goblin.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package Prototype.Shallow;

import java.util.UUID;

public class Goblin implements Hero {

UUID heroID;
int attack;
int defense;
int magic;
int health;

public Goblin(){
this.attack= HeroProperties.GOBLIN_ATTACK.getValue();
this.defense= HeroProperties.GOBLIN_DEFENSE.getValue();
this.magic= HeroProperties.GOBLIN_MAGIC.getValue();
this.health=100;
}
@Override
public void setID(UUID heroID) {
this.heroID = heroID;
}
@Override
public Goblin clone() throws CloneNotSupportedException {
try {
return (Goblin) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
@Override
public String toString() {
return "Goblin{" +
"attack=" + attack +
", defense=" + defense +
", magic=" + magic +
", health=" + health +
'}';
}
}

and finally the wizard

Wizard.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package Prototype.Shallow;

import java.util.UUID;

public class Wizard implements Hero {

UUID heroID;
int attack;
int defense;
int magic;
int health;

public Wizard() {
this.attack= HeroProperties.WIZARD_ATTACK.getValue();
this.defense= HeroProperties.WIZARD_DEFENSE.getValue();
this.magic= HeroProperties.WIZARD_MAGIC.getValue();
this.health=100;
}
@Override
public void setID(UUID heroID) {
this.heroID = heroID;
}
@Override
public Wizard clone() throws CloneNotSupportedException {
try {
return (Wizard) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
@Override
public String toString() {
return "Wizard{" +
"attack=" + attack +
", defense=" + defense +
", magic=" + magic +
", health=" + health +
'}';
}
}

With the above in place, we go to the HeroWareHouse, which will act as a container of the Heroes “embryos”.
The warehouse is a simple hash map, and as we will only be storing one hero of each, the class name is used as the key. Not very sophisticated, but it does serves the purpose. :)

HeroWareHouse.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package Prototype.Shallow;

import java.util.HashMap;
import java.util.Map;

public class HeroWarehouse {

private static Map<String, Hero> heroShop=new HashMap<>();

public Hero add(Hero hero){
String className = hero.getClass().getSimpleName();
heroShop.put(className,hero);
return heroShop.get(className);
}

public Hero get(String Herotype){
return heroShop.get(Herotype);
}
}

The game board will be a 20 x 20 grid represented by a two-dimensional array. Whenever an Hero is added a new UUID for that hero will be generated. A simple while loop is used to ensure that the square is empty, so that no Hero is overwritten.

Board.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package Prototype.Shallow;

import java.util.Random;
import java.util.UUID;

public class Board {

private static Hero[][] board= new Hero[20][20];
private static Random rand=new Random();

public Hero add (Hero hero){

int x=0,y=0;
boolean positionAvailable=false;

while(positionAvailable==false) {
x=randomX();
y=randomY();
positionAvailable=isEmpty(x,y);
}

hero.setID(UUID.randomUUID());
board[x][y]=hero;
return board[x][y];
}

public int randomX(){
return rand.nextInt(21);
}

public int randomY(){
return rand.nextInt(21);
}

public boolean isEmpty(int x, int y){
if (board[x][y]==null){
return true;
}
return false;
}
}

So a little app to test all this

HeroWareHouse.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package Prototype.Shallow;

public class App {

public static void main(String[] args) throws CloneNotSupportedException {

HeroWarehouse heroWarehouse = new HeroWarehouse();

Hero elf = new Elf();
Hero goblin = new Goblin();
Hero wizard = new Wizard();
heroWarehouse.add(elf);
heroWarehouse.add(goblin);
heroWarehouse.add(wizard);

Board gameboard = new Board();

for (int i = 0; i <= 5; i++) {

gameboard.add(heroWarehouse.get("Wizard").clone());
gameboard.add(heroWarehouse.get("Goblin").clone());
gameboard.add(heroWarehouse.get("Elf").clone());

}
}
}

As said earlier in this article, we are pretending that heroes values are being fetched from a database. So, we instantiate just one of each hero (Elf,Goblin and wizard), and store one copy of each in the heroWarehouse.

Then after instantiating the game board, we fetch the heroes from the warehouse, and clone them, thus avoiding to repeat the process of fetching values from the database(here an imaginary one)

We will come back to the prototype pattern, in another article with an example of deep copy cloning.

An example for the code in this article can be found in git-hub

Share