Prototype pattern — [Notes]

Tarun Jain
7 min readSep 1, 2022

--

Copying an object “from the outside” isn’t always possible. [some fields may be private and not visible]

Prototype results in a cloned object which is different from the original object. The state of the original is the same as the clone, at the time of cloning. Thereafter each object may undergo a state change

  • A prototype design pattern is one of the creational design patterns. It allows us to specify objects which are prototypes of original objects. In other words, it will enable us to copy existing objects instead of making new instances.
  • The prototype is a creational design pattern that allows cloning objects, even complex ones, without coupling to their specific classes

The Prototype pattern delegates the cloning process to the actual objects that are being cloned.

  • The pattern declares a standard interface for all objects that support cloning.
    → Usually, such an interface contains just a single clone method.

An object that supports cloning is called a prototype.

  • ✅ When your objects have dozens of fields and hundreds of possible configurations, cloning them might serve as an alternative to subclassing.
    → When instances of a class can have one of only a few different combinations of states. 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.
  • Specifying new objects by varying structure — Many programs construct objects from parts and subparts. Such programs frequently allow you to create complicated, user-defined structures to reuse a specific subcircuit.
    → Prototypes are useful when object initialization is expensive, and you anticipate a few variations on the initialization parameters. In this context, a Prototype can avoid expensive “creation from scratch”, and support cheap cloning of a pre-initialized prototype.
  • ✅ It reduces the need for sub-classing.
  • ✅ It hides the complexities of creating objects.
  • ✅ Classes are instantiated at runtime.
  • ✅ Used when the cost of creating an object is expensive or complicated.
  • ✅ Used when the client application needs to be unaware of object creation and representation.
  • ✅ cloning saves memory[How?]
  • ✅ This pattern is handy when our new object is only slightly different from our existing one. In some cases, instances may have only a few combinations of states in a class. So instead of creating new instances, we may create the instances with the appropriate state beforehand and then clone them whenever we want.
    → Useful when objects should be similar or the same [if objects are very different then using a prototype pattern would be less useful]
  • Adding and removing products at run-time — By registering a prototype instance with the client, you may easily introduce a new concrete product class into a system. Because a client can install and delete prototypes at run-time, this style is a little more adaptable than other creational models.
    → classes are loaded dynamically.
    → classes to instantiate are specified at run-time, for example, by dynamic loading.
  • Performance: Cloning (using MemberwiseClone) is considerably less expensive than creating a new object afresh (with new operator). Note that one needs to override the MemberwiseClose() to perform a deep copy.
Toy anotherPlasticToy = (Toy) Toy.copy(); anotherPlasticToy.setPosition(otherPosition); //only position is different

Prototype pattern in Java

  • Mark classes with a “Cloneable” interface to make users aware that this class can be cloned
  • No method in Cloneable interface
  • clone method is the “Object” class method which does a shallow copy, it works fine if all the class variables are immutable fields or primitive values
  • Copy can also be done using “Copy Contractors”
  • Custom clone method: Also can do copying using own implemented clone method

Code

source— https://sourcemaking.com/design_patterns/prototype/java/1

//Source - https://sourcemaking.com/design_patterns/prototype/java/1interface Person {
Person clone();
}

class Tom implements Person {
private final String NAME = "Tom";

@Override
public Person clone() {
return new Tom();
}

@Override
public String toString() {
return NAME;
}
}

class Dick implements Person {
private final String NAME = "Dick";

@Override
public Person clone() {
return new Dick();
}

@Override
public String toString() {
return NAME;
}
}

class Harry implements Person {
private final String NAME = "Harry";

@Override
public Person clone() {
return new Harry();
}

@Override
public String toString() {
return NAME;
}
}

class Factory {
private static final Map<String, Person> prototypes = new HashMap<>();

static {
prototypes.put("tom", new Tom());
prototypes.put("dick", new Dick());
prototypes.put("harry", new Harry());
}

public static Person getPrototype(String type) {
try {
return prototypes.get(type).clone();
} catch (NullPointerException ex) {
System.out.println("Prototype with name: " + type + ", doesn't exist");
return null;
}
}
}

public class PrototypeFactory {
public static void main(String[] args) {
if (args.length > 0) {
for (String type : args) {
Person prototype = Factory.getPrototype(type);
if (prototype != null) {
System.out.println(prototype);
}
}
} else {
System.out.println("Run again with arguments of command string ");
}
}
}

Rules of thumb

source — https://en.wikipedia.org/wiki/Prototype_pattern

Sometimes creational patterns overlap — there are cases when either prototype or abstract factory would be appropriate. At other times, they complement each other: abstract factory might store a set of prototypes from which to clone and return product objects.[2]: 126 Abstract factory, builder, and prototype can use singleton in their implementations.[2]: 81, 134 Abstract factory classes are often implemented with factory methods (creation through inheritance), but they can be implemented using prototype (creation through delegation).[2]: 95

Often, designs start out using Factory Method (less complicated, more customizable, subclasses proliferate) and evolve toward abstract factory, prototype, or builder (more flexible, more complex) as the designer discovers where more flexibility is needed.[2]: 136

Prototype does not require subclassing, but it does require an “initialize” operation. Factory method requires subclassing, but does not require initialization.[2]: 116

Designs that make heavy use of the composite and decorator patterns often can benefit from Prototype as well.[2]: 126

The rule of thumb could be that you would need to clone() an Object when you want to create another Object at runtime that is a true copy of the Object you are cloning. True copy means all the attributes of the newly created Object should be the same as the Object you are cloning. If you could have instantiated the class by using new instead, you would get an Object with all attributes as their initial values. For example, if you are designing a system for performing bank account transactions, then you would want to make a copy of the Object that holds your account information, perform transactions on it, and then replace the original Object with the modified one. In such cases, you would want to use clone() instead of new.

How it Reduces Subclassing

Source https://stackoverflow.com/a/5739564/3759177

Let’s say you’re making Minecraft and you’re using the prototype pattern for each different kind of block (e.g. dirt, stone, etc). All the prototype objects are actually of the same class Block, but each object has different properties set on it so that it looks and behaves differently, for example:

prototypes.dirt = new Block;
prototypes.dirt.texture = new Image("dirt.jpg");
prototypes.dirt.hardness = 1;
prototypes.stone = new Block;
prototypes.stone.texture = new Image("stone.jpg");
prototypes.stone.hardness = 9;

So instead of subclassing where you would write new DirtBlock or new StoneBlock, you would instead write prototypes.dirt.clone() or prototypes.stone.clone(). No subclassing is required, but you still have the option to subclass if need be.

Factory vs Prototype

Answer #1
Source — https://stackoverflow.com/a/5739479/3759177

The difference between the two patterns is the fact that the Factory Method concentrates on creating one object of a non-existing object type as a fresh creation (by understanding the exact sub-type of the Creator class). The Prototype pattern uses the class itself, especially the derived class for self-duplication action.

Answer #2
Source — https://stackoverflow.com/a/5739479/3759177

As for when to choose the prototype pattern instead of a factory pattern, there are two situations I can think of where they differ:

  1. You can iterate over a list of prototypes, but you can’t iterate over all the methods on an abstract factory^. Continuing from the code above, you could create a random block like so:

prototypes.allValues().objectAtIndex(rand() % prototypes.size()).clone();

If you were using the factory method to make blocks, it would be harder to get a random block.

2. Where the creation of an object is expensive, but copying is cheap, the prototype pattern will be more efficient. For example, take this factory method:

Image loadUserImage() { 
//loads from disk. will be slow
return new JPEGImage("path/to/user/image.jpg");
}

If this method is going to be called repeatedly, it would be more efficient to use a prototype like so:

Image loadUserImage() {     
//copy in memory. will be fast
return userImagePrototype.clone();
}

White lie

source — https://stackoverflow.com/a/13575709/3759177s

The gain in efficiency of using a prototype is questionable in my mind. There will be no efficiency gain because in most languages the clone method itself executes a call to new in order to construct a new object instance of itself.

The only benefit that I see in using the prototype pattern is one of convenience, you know that clone will give you an exact copy of the object which frees you from having to set the attributes of the new object to the same values yourself and possibly struggling with deep copy.

Credits/Source

--

--