It’s a creational design pattern, that emphasizes on effective ways to clone objects.

At times in our codebase, we might need to create a copy of object with same state but use it for a different purpose. One way to copy an object is to do field assignments from source object to target object. But, copying fields explicitly in client code has got some challenges as listed below.

  • Field assignment from source to target irrespective of the DataType results in shallow copy. This works when types are primitives, but when they are of Object types, straight field to field assignment results in a use case like two references referring to same object. Java doesn’t guarantee defensive copy by default.
  • When only a single field differs between source and target, copying all fields is not methodical.
  • Some objects may have private fields.
  • What if we know only the interface type of the object, not the concrete type?

These challenges can be addressed by implementing prototype pattern wherein object creation process is delegated to the object itself instead of handling it at client side, and it’s the responsibility of the object to ensure that every field is referring to a dedicated object that is Defensive copy/Deep Copy.

Here is the sample implementation of Prototype Pattern

Factory method pattern

public class Make {

  private String brand;
  private int estYear;

  public Make(String brand, int estYear) {
    this.brand = brand;
    this.estYear = estYear;
  }

  public Make(Make make) { // Copy Constructor
    this(make.brand, make.estYear);
  }
}

public interface Car { // Car Prototype
  Car clone();
}
public class HatchBack implements Car {

  private Make make;
  private String model;

  public HatchBack(Make make, String model) {
    this.make = make; 
    this.model = model;
  }

  public HatchBack(HatchBack hatchBack) { // CopyConstructor
    // Defensive copy
    this(new Make(hatchBack.make), hatchBack.model);
  }

  @Override
  public HatchBack clone() {
    return new HatchBack(this);
  }
}
public class Sedan implements Car {

  private Make make;
  private String model;
  private String color;

  public Sedan(Make make, String model, String color) {
    this.make = make; 
    this.model = model;
    this.color = color;
  }

  public Sedan(Sedan sedan) { // CopyConstructor
    // Defensive copy
    this(new Make(sedan.make), sedan.model, sedan.color);
  }

  @Override
  public Sedan clone() {
    return new Sedan(this);
  }
}
public class Driver {

  public static void main(String[] args) {

    Make make = new Make("nameless", 2023);

    HatchBack hatchBack = new HatchBack(make, "goodcar");
    // Create 2 Hatchbacks with above HatchBack specs
    /** Here we can either use CopyConstrutor or clone method */
    HatchBack hatchBack1 = new HatchBack(hatchBack);
    HatchBack hatchBack2 = hatchBack.clone();

    Sedan sedan = new Sedan(make, "nicecar", "grey");
    // Create 2 Sedans with above Sedan specs
    /** Here we can either use CopyConstrutor or clone method */
    Sedan sedan1 = new Sedan(sedan);
    Sedan sedan2 = sedan.clone();

    /**
     * Protoype Pattern Applicability: When we want to create a copy of polymorphic array
     */
    List<Car> cars = new ArrayList<>();
    cars.add(hatchBack);
    cars.add(sedan);

    // Create a defensive copy of cars list
    List<Car> carsCopy = new ArrayList<>();
    for (Car car : cars) {
      carsCopy.add(car.clone()); // delegating the deep copy responsibility to Car subtypes
    }
  }
}

References:

  1. Prototype Pattern
  2. Copy Constructor is recommended instead of clone
  3. Copy Constructor vs Prototype pattern