Skip to main content

Prototype Pattern

  • It is a Creational design pattern that lets you create new objects by cloning existing ones, instead of instantiating them from scratch.

Useful in situations

  • When creating a new object is expensive, time-consuming and resource intensive.
  • Want to avoid complex object initialization logic.

Challenges of Cloning Objects

  • Create a new object and manually copy each field from the original object to new one.
  • Most of the systems many field are private or hidden behind encapsulation that means our cloning logic can't access them directly.
  • Even if we can access all properties still we need to know concrete class of the object to instantiate a copy.

One small solution

  • Instead of copying object manually we let object know how to clone it, object will have a function like copy or clone in itself.
  • It has many benefits
    • No need to know concrete class
    • presevers encapsulation

Problem

  • Suppose we have a enemy class and we need to spawn it in some code so we will do it like
// type speed ... weaponType
Enemy flying1 = new Enemy("Flying", 100, 10.5, false, "Laser");
Enemy flying2 = new Enemy("Flying", 100, 10.5, false, "Laser");
  • To solve above problem we will use the prototype pattern.

The Prototype pattern

  • The Prototype pattern specifies the kinds of objects to create using a prototypical instance and creates new objects by copying (cloning) this prototype.

Class Diagram

  • Defines the Prototype Interface declares a clone method which concrete class should implement.
  • Implement Concrete Prototypes which will implement logic to clone itself.
  • When Client need it just ask object to clone itself.

Optional : with registry

  • As our system grows with more enemy types or components, we can introduce a Prototype Registry:
    • It stores a collection of named prototypes (e.g., "flying", "armored").
    • Clients request a clone of a named prototype using a simple key.
    • This separates the concern of what to create from how to configure it.

Implementation

Define Prototype

public interface EnemyPrototype {
EnemyProtype clone();
}

Create Concrete Class

public class Enemy implements EnemyPrototype {
private String type;
private int health;
private double speed;
private boolean armoured;
private String weapon;

public Enemy(...takes all variable){
...assign variable to local ones
}

@Override
Enemy clone(){
return new Enemy(type,health,speed,armoured,weapon);
}

.... remaining methods

}
  • while we are cloning we can have issue with cloning if have some non-primitive types to clone.
    • Shallow copy
      • It’s fine if all fields are primitives or immutable (like String). But if Enemy had a field like a List, both the original and cloned enemies would share the same list object, which can cause subtle bugs.
    • Deep copy
      • If your object contains mutable reference types, you should create a deep copy in the copy constructor. For e.g.:
      this.inventory = new ArrayList<>();
      for (Item item : source.inventory) {
      this.inventory.add(item.clone()); // Assuming Item also implements clone()
      }

Optional: Creat registry

  • stores pre-configured prototype instances. This keeps your code organized, especially when you have many types of enemies.
public class EnemyRegistry {
private Map<String,Enemy> prototypes = new HashMap<>();

public void register(String key, Enemy prototype){
prototype.push(key,prototype);
}

public Enemy get(String key){
Enemy prototype = prototypes.get(key);
if(prototype!=null){
return prototype.clone();
}
// throw for no registered exception
}
}

Copy with use of registry

public class Game {
psvm(...){
EnemyRegistry registry = new EnemyRegistry();

registry.register("flying",new Enemy(..enemy with all input));
registry.register(...go for armored);

// Clone

Enemy e1 = registry.get("flying");
Enemy e2 = registry.get("armored");


... we can use further
}
}