Skip to main content

Prototype

CreationalGang Of FourInstantiationObject compositionPolymorphismAbout 3 min

Also known as

  • Clone

Intent

The Prototype pattern is used to specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.

Explanation

Real-world example

Remember Dolly? The sheep that was cloned! Let's not get into the details but the key point here is that it is all about cloning.

In plain words

Create an object based on an existing object through cloning.

Wikipedia says

The prototype pattern is a creational design pattern in software development. It is used when the type of objects to create is determined by a prototypical instance, which is cloned to produce new objects.

Programmatic Example

In Java, the prototype pattern is recommended to be implemented as follows. First, create an interface with a method for cloning objects. In this example, Prototype interface accomplishes this with its copy method.

public abstract class Prototype<T> implements Cloneable {
    @SneakyThrows
    public T copy() {
        return (T) super.clone();
    }
}

Our example contains a hierarchy of different creatures. For example, let's look at Beast and OrcBeast classes.

@EqualsAndHashCode(callSuper = false)
@NoArgsConstructor
public abstract class Beast extends Prototype<Beast> {
  public Beast(Beast source) {}
}

@EqualsAndHashCode(callSuper = false)
@RequiredArgsConstructor
public class OrcBeast extends Beast {

  private final String weapon;

  public OrcBeast(OrcBeast orcBeast) {
    super(orcBeast);
    this.weapon = orcBeast.weapon;
  }

  @Override
  public String toString() {
    return "Orcish wolf attacks with " + weapon;
  }
}

We don't want to go into too many details, but the full example contains also base classes Mage and Warlord and there are specialized implementations for those for elves in addition to orcs.

To take full advantage of the prototype pattern, we create HeroFactory and HeroFactoryImpl classes to produce different kinds of creatures from prototypes.

public interface HeroFactory {
  Mage createMage();
  Warlord createWarlord();
  Beast createBeast();
}

@RequiredArgsConstructor
public class HeroFactoryImpl implements HeroFactory {

  private final Mage mage;
  private final Warlord warlord;
  private final Beast beast;

  public Mage createMage() {
    return mage.copy();
  }

  public Warlord createWarlord() {
    return warlord.copy();
  }

  public Beast createBeast() {
    return beast.copy();
  }
}

Now, we are able to show the full prototype pattern in action producing new creatures by cloning existing instances.

    var factory = new HeroFactoryImpl(
        new ElfMage("cooking"),
        new ElfWarlord("cleaning"),
        new ElfBeast("protecting")
    );
    var mage = factory.createMage();
    var warlord = factory.createWarlord();
    var beast = factory.createBeast();
    LOGGER.info(mage.toString());
    LOGGER.info(warlord.toString());
    LOGGER.info(beast.toString());

    factory = new HeroFactoryImpl(
        new OrcMage("axe"),
        new OrcWarlord("sword"),
        new OrcBeast("laser")
    );
    mage = factory.createMage();
    warlord = factory.createWarlord();
    beast = factory.createBeast();
    LOGGER.info(mage.toString());
    LOGGER.info(warlord.toString());
    LOGGER.info(beast.toString());

Here's the console output from running the example.

Elven mage helps in cooking
Elven warlord helps in cleaning
Elven eagle helps in protecting
Orcish mage attacks with axe
Orcish warlord attacks with sword
Orcish wolf attacks with laser

Class diagram

alt text
Prototype pattern class diagram

Applicability

  • When the classes to instantiate are specified at run-time, for example, by dynamic loading.
  • To avoid building a class hierarchy of factories that parallels the class hierarchy of products.
  • When instances of a class can have one of only a few different combinations of state. It may be more convenient to install a corresponding number of prototypes and clone them rather than instantiating the class manually, each time with the appropriate state.
  • When object creation is expensive compared to cloning.
  • When the concrete classes to instantiate are unknown until runtime.

Known uses

  • In Java, the Object.clone() method is a classic implementation of the Prototype pattern.
  • GUI libraries often use prototypes for creating buttons, windows, and other widgets.
  • In game development, creating multiple objects (like enemy characters) with similar attributes.

Consequences

Benefits:

  • Hides the complexities of instantiating new objects.
  • Reduces the number of classes.
  • Allows adding and removing objects at runtime.

Trade-offs:

  • Requires implementing a cloning mechanism which might be complex.
  • Deep cloning can be difficult to implement correctly, especially if the classes have complex object graphs with circular references.

Credits