LLD - Prototype Design Pattern
Understanding Prototype Pattern
Prototype Pattern comes under the Creational Design Pattern which caters to the creation of objects.
It enables cloning or copying data from the existing object and storing it in our newly created object.
Blueprint of Prototype Design Patten:
Prototype Interface: To define a method for cloning
Concrete Prototype: Classes that implement prototype interface to clone itself
Client: Class that uses prototype to create new objects
Case Study: Monsters in a Game
Deep Copy Implementation in a Game for Creating Monsters.
In many games, characters such as Monsters get stronger as the level of the game increases. In this scenario, the Prototype pattern can be used to create multiple instances of Monsters where their strength and other abilities increase as the game levels up.
Prototype Pattern is useful :
When creating new objects is expensive or complicated
When we have to create multiple variations of an object
When we want to create a deep copy with nested fields
Code
public interface Prototype {
Prototype clone();
}
public class GameMonster implements Prototype {
private String name;
private MonsterType type;
private int strength;
private int level;
private SecretStrength secretStrength;
public GameMonster(String name, MonsterType type, int strength, int level, SecretStrength secretStrength) {
this.name = name;
this.type = type;
this.strength = strength;
this.level = level;
this.secretStrength = secretStrength;
}
@Override
public GameMonster clone() {
var secretStrengthClone = secretStrength.clone();
return new GameMonster(this.name, this.type, this.strength, this.level, secretStrengthClone);
}
public void setStrength(int strength) {
this.level = strength;
}
public void setSecretStrength(SecretStrength secretStrength) {
this.secretStrength = secretStrength;
}
public SecretStrength getSecretStrength() {
return secretStrength;
}
@Override
public String toString() {
return "GameMonster{" +
"name='" + name + '\'' +
", type=" + type +
", strength=" + strength +
", level=" + level +
", secretStrength=" + secretStrength +
'}';
}
}
public enum MonsterType {
LAND,
AIR
}
public class SecretStrength implements Prototype {
private int secretPower;
private int agility;
private int speed;
public SecretStrength(int secretPower, int agility, int speed) {
this.secretPower = secretPower;
this.agility = agility;
this.speed = speed;
}
@Override
public SecretStrength clone() {
return new SecretStrength(this.secretPower, this.agility, this.speed);
}
public void upgrade(int gameLevel) {
secretPower = this.secretPower * gameLevel;
agility = this.agility * gameLevel;
speed = this.speed * gameLevel;
}
@Override
public String toString() {
return "SecretStrength{" +
"secretPower=" + secretPower +
", agility=" + agility +
", speed=" + speed +
'}';
}
}
import java.util.HashMap;
import java.util.Map;
public class MonsterRegistry {
private static final Map<MonsterType, GameMonster> monsterMap = new HashMap<>();
public static void addMonster(MonsterType type, GameMonster monster) {
monsterMap.put(type, monster);
}
public static GameMonster getMonster(MonsterType type, int gameLevel) {
var currentMonster = monsterMap.get(type);
GameMonster cloneMonster = currentMonster.clone();
cloneMonster.setStrength(gameLevel * 7); // increasing the strength as per game level
cloneMonster.getSecretStrength().upgrade(gameLevel * 7);
return cloneMonster;
}
}
public class Main {
public static void main(String[] args) {
System.out.println("Implementing Prototype Pattern: ");
// Creating Base Monster - Goblin and Dragon
SecretStrength secretStrength = new SecretStrength(10, 20, 30);
GameMonster greenGoblin = new GameMonster("Green Goblin", MonsterType.LAND, 10, 1, secretStrength);
GameMonster dragon = new GameMonster("Draco", MonsterType.AIR, 20, 2, secretStrength);
MonsterRegistry.addMonster(MonsterType.LAND, greenGoblin);
MonsterRegistry.addMonster(MonsterType.AIR, dragon);
System.out.println("Green Goblin: " + greenGoblin);
System.out.println("Draco: " + dragon);
// Upgrading Monsters for higher game level
System.out.println("Level Up Monsters: ");
GameMonster redGoblin = MonsterRegistry.getMonster(MonsterType.LAND, 4);
GameMonster redDraco = MonsterRegistry.getMonster(MonsterType.AIR, 6);
System.out.println("Level 4 Red Goblin: " + redGoblin);
System.out.println("Level 4 Red Draco: " + redDraco);
}
}
Github Source Code: LLD for Game Monster
Shallow vs Deep Copy
Shallow Copy: It duplicates the object but only copies the reference of the nested object resulting in multiple shared states between the original and copied object
Deep Copy: It duplicates the object and all the nested objects creating entirely independent instances, so changes to one will not affect the other
class Address {
String street;
Address(String street) {
this.street = street;
}
}
class Person {
String name;
Address address; // Reference to a mutable object
Person(String name, Address address) {
this.name = name;
this.address = address;
}
// Method to create a shallow copy
Person shallowCopy() {
return new Person(this.name, this.address); // Copies the reference of address
}
// Method to create a deep copy
Person deepCopy() {
return new Person(this.name, new Address(this.address.street)); // Creates a new Address object
}
}
Refer: https://www.cs.utexas.edu/~scottm/cs307/handouts/deepCopying.htm
Subscribe to my newsletter
Read articles from Manish Pushkar directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Manish Pushkar
Manish Pushkar
Software Engineer - Backend