This is a creational pattern, the prototype pattern is designed to instantiate a class by copying, or cloning, the properties of an existing object. The new object is an exact copy of the prototype but permits modification without altering the original.
The prototype pattern usually generates deep copies of the objects, not just shallow copies of it (this depends on the implementation we decide to do and our requirements). A shallow copy duplicates all of the object’s properties. If any property contains a reference type, the reference is copied. This means that changes to the referenced object are visible in both the clone and the original object. A deep copy clones the main object and all child objects. Any properties of reference types are also cloned, giving a truly independent copy.
Elements involved:
- Prototype: This is an abstract class or an interface if the objects does not share properties for the types of object that can be generated and cloned. The class will contain a single method named “clone” that returns a prototype object.
- ConcretePrototype (A/B): This class inherits or implements the prototype class. In addition to copying the original object’s data to the clone, this method may also handle some edge cases of the cloning process related to cloning linked objects, untangling recursive dependencies, etc.
We should use the Prototype design pattern when:
It is generally used for complex classes or for classes that are costly to instantiate.
Let’s see some code:
public abstract class Prototype {
private int propertyA;
private int propertyB;
private String propertyC;
public Prototype() {
}
public Prototype(final Prototype prototype) {
propertyA = prototype.propertyA;
propertyB = prototype.propertyB;
propertyC = prototype.propertyC;
}
public abstract Prototype clone();
// Getters & Setters
}
public class ConcretePrototype1 extends Prototype {
private String ownProperty1;
public ConcretePrototype1() {
}
public ConcretePrototype1(final ConcretePrototype1 prototype) {
super(prototype);
ownProperty1 = prototype.ownProperty1;
}
@Override
public Prototype clone() {
return new ConcretePrototype1(this);
}
// Getters & Setters
}
public class ConcretePrototype2 extends Prototype {
private String ownProperty2;
public ConcretePrototype2() {
}
public ConcretePrototype2(final ConcretePrototype2 prototype) {
super(prototype);
ownProperty2 = prototype.ownProperty2;
}
@Override
public Prototype clone() {
return new ConcretePrototype2(this);
}
// Getters & Setters
}
public class Main {
public static void main(String[] args) {
final List<Prototype> prototypes = new ArrayList<>();
final ConcretePrototype1 cp1 = new ConcretePrototype1();
final ConcretePrototype2 cp2 = new ConcretePrototype2();
prototypes.add(cp1);
prototypes.add(cp2);
// We can now copy the list without worrying about what
// specific type is the object
final List<Prototype> newPrototypes = new ArrayList<>();
for (final Prototype p : prototypes) {
newPrototypes.add(p.clone());
}
}
}
You can find the code in my GitHub repository design-apperns.