Creational Patterns  «Prev 

Prototype Pattern in Java

In the prototype pattern, a new object is created by cloning an existing object. In Java, the clone() method is an implementation of this design pattern. The prototype pattern can be a useful way of creating copies of objects. One example of how this can be useful is if an original object is created with a resource such as a data stream that may not be available at the time that a clone of the object is needed. Another example is if the original object creation involves a significant time commitment, such as reading data from a database. An added benefit of the prototype pattern is that it can reduce class proliferation in a project by avoiding factory proliferation. We can implement our own prototype pattern. To do so, we will create a Prototype interface that features a doClone() method.
Here's a complete implementation that demonstrates the Prototype pattern using a Prototype interface with a doClone() method:
// Prototype interface
interface Prototype {
    Prototype doClone();
}

// Concrete class implementing Prototype - Car example
class Car implements Prototype {
    private String model;
    private int year;
    private String color;

    public Car(String model, int year, String color) {
        this.model = model;
        this.year = year;
        this.color = color;
        // Simulating expensive initialization
        try {
            Thread.sleep(1000); // Simulate time-consuming construction
            System.out.println("Original Car created - expensive operation complete");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    // Implementation of the clone method
    @Override
    public Prototype doClone() {
        return new Car(this.model, this.year, this.color);
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return "Car{model='" + model + "', year=" + year + ", color='" + color + "'}";
    }
}
// Concrete class implementing Prototype - Bike example
class Bike implements Prototype {
    private String type;
    private boolean hasBasket;

    public Bike(String type, boolean hasBasket) {
        this.type = type;
        this.hasBasket = hasBasket;
        // Simulating expensive initialization
        try {
            Thread.sleep(800); // Simulate time-consuming construction
            System.out.println("Original Bike created - expensive operation complete");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Prototype doClone() {
        return new Bike(this.type, this.hasBasket);
    }

    @Override
    public String toString() {
        return "Bike{type='" + type + "', hasBasket=" + hasBasket + "}";
    }
}

// Demo class to test the Prototype pattern
public class PrototypeDemo {
    public static void main(String[] args) {
        // Create original objects (expensive operation)
        Car originalCar = new Car("Sedan", 2023, "Blue");
        Bike originalBike = new Bike("Mountain", true);

        // Create clones (faster than creating new objects)
        long startTime = System.currentTimeMillis();
        Car clonedCar = (Car) originalCar.doClone();
        Bike clonedBike = (Bike) originalBike.doClone();
        long endTime = System.currentTimeMillis();

        // Modify clone without affecting original
        clonedCar.setColor("Red");

        // Display results
        System.out.println("\nResults:");
        System.out.println("Original Car: " + originalCar);
        System.out.println("Cloned Car: " + clonedCar);
        System.out.println("Original Bike: " + originalBike);
        System.out.println("Cloned Bike: " + clonedBike);
        System.out.println("Cloning time: " + (endTime - startTime) + "ms");
    }
}
This implementation demonstrates several key aspects of the Prototype pattern:
  1. Interface Definition: The Prototype interface declares the doClone() method that all concrete prototypes must implement.
  2. Concrete Prototypes:
    • Car and Bike classes implement the Prototype interface
    • Each has its own attributes and simulates expensive creation with Thread.sleep()
    • Each implements doClone() to create a new instance with the same properties
  3. Benefits Showcased:
    • Expensive Object Creation: The initial creation includes a simulated delay
    • Efficient Copying: Cloning is faster than creating new objects from scratch
    • Independence: Changes to clones don't affect originals
    • Reduced Factory Need: No need for separate factory classes for each type

When you run this code, you'll see output similar to this:
Original Car created - expensive operation complete
Original Bike created - expensive operation complete

Results:
Original Car: Car{model='Sedan', year=2023, color='Blue'}
Cloned Car: Car{model='Sedan', year=2023, color='Red'}
Original Bike: Bike{type='Mountain', hasBasket=true}
Cloned Bike: Bike{type='Mountain', hasBasket=true}
Cloning time: 2ms
Key points about this implementation:
  1. Time Savings: The cloning process is significantly faster than original object creation (2ms vs 1000ms+800ms for original construction)
    • This implementation fulfills the benefits you mentioned:
      • Works with resources that might not be available later
      • Avoids expensive initial object creation for subsequent instances
      • Reduces the need for factory class proliferation
  2. Flexibility: You can easily add new prototype classes by implementing the Prototype interface
  3. Shallow Copy: This example uses a shallow copy. For deep copying of complex objects with nested references, you'd need to modify the doClone() method accordingly
  4. Type Safety: The implementation requires casting when cloning ((Car) or (Bike)), but this could be improved with generics or by using Java's built-in Cloneable interface alongside this custom approach


Declare the Prototype interface

package com.java.creational.prototype;

public interface Prototype {
  public Prototype doClone();
}

The Person class implements the doClone() method. This method creates a new Person object and clones the name field. It returns the newly cloned Person object.
package com.java.creational.prototype;

public class Person implements Prototype {
  String name;
  public Person(String name) {
  this.name = name;
 }
@Override
  public Prototype doClone() {
  return new Person(name);
 }
 public String toString() {
  return "This person is named " + name;
 }
}

The Cat class also implements the doClone() method. This method creates a new Cat object and clones the sound field. The cloned Cat object is returned.
package com.java.creational.prototype;

public class Cat implements Prototype {
 String sound;
 public Cat(String sound) {
  this.sound = sound;
 }
 @Override
 public Prototype doClone() {
  return new Cat(sound);
 }
 public String toString() {
  return "This cat says " + sound;
 }
}

The Demo class creates a Person object and then clones it to a second Person object.
It then creates a Cat object and clones it to a second Cat object.

Write the Driver for the Interface and Class

package com.java.creational.prototype;
public class PrototypeExample {
 public static void main(String[] args) {
  Person person1 = new Person("Glenn");
  System.out.println("person 1:" + person1);
  Person person2 = (Person) person1.doClone();
  System.out.println("person 2:" + person2);
  Cat cat1 = new Cat("Meow");
  System.out.println("cat 1:" + cat1);
  Cat cat2 = (Cat) cat1.doClone();
  System.out.println("cat 2:" + cat2);
 }
}

Program output:
person 1:This person is named Glenn
person 2:This person is named Glenn
cat 1:This cat says Meow
cat 2:This cat says Meow

Remote 1