In a drawing program, it's useful to be able to copy and paste shapes. If you were making your own drawing software and implemented copy and paste, you'll probably find that the easiest thing to do is create a function for each shape that transfers it's data to a new shape instance. This process of copying the attributes of an object and returning a new instance is called cloning, and it is the core principle at work in the prototype method.

Assumptions

What is The Prototype Design Pattern?

The prototype design pattern is a creational pattern that focuses on cloning an instance of a complex object, known as the prototype.

<div style="width:100%; margin:auto;text-align:center;"><img src="https://www.devmaking.com/img/topics/designpatterns/PrototypePattern_01.png" alt="prototype pattern UML diagram" style="max-width:95%;"> </div>

Cloning an object allows an exact copy of the prototype to be made at runtime without needing to specify a new subclass of an object to be instantiated.

Conceptualization

For this example, imagine that you are creating a game about a computer manufacturing company. In this game, you create a "prototype" computer with all the components and specs, then send the prototype to be manufactured on a large scale.

When the manufacturer receives the prototype, they'll clone the computer based on the attributes of the prototype. Let's begin modeling:

interface IComputer {
   IComputer clone();
 }

class Desktop implements IComputer {
   String cpu;
   String gpu;
   int RAMgigs;
   
   // Basic constructor
   public Desktop() {
   }
   
   // Constructor that accepts a prototype, used by the clone method
   public Desktop(Desktop prototype) {
     this.cpu = prototype.cpu;
     this.gpu = prototype.gpu;
     this.RAMgigs = prototype.RAMgigs;
   }
   
   // Our clone method being implemented
   public Desktop clone() {
     return new Desktop(this);
   }
   
 }

> Many modern languages have their own interfaces or functions to implement the clone() method. While those libraries are implemented slightly different, this can still help give the idea behind the concept!

So far, we have created our interface, IComputer, and implemented it in the class Desktop. From here, whenever we want to copy a prototype instance of a desktop, we just need to call on the clone() method:

static void main(String[] args) {
    // This desktop instance is acting as our prototype.
   Desktop prototype = new Desktop();
   prototype.cpu = "i12";
   prototype.gpu = "ix 4096";
   prototype.RAMgigs = 128;
   
   // Now we can get a clone of the prototype for much less work!
   Desktop ultraComputer = prototype.clone();
 }

Caching

While not a requirement of the prototype pattern, it's popular to pair a prototype with a "cache" or a "store", similar to a <a href="https://www.devmaking.com/learn/design-patterns/factory-pattern/" target="_blank" style="color:inherit;">simple factory</a>. Doing this lets us build up a catalogue of prototypes to pull from quickly in future cases.

Building on our example from earlier, let's say that our computer manufacturer company has built up a list of a few computers now. Instead of keeping them all in the client code, we can abstract them to their own container class to be referenced at a later time:

/* pseudo-code */
class ComputerCatalogue {
   // This will hold our prototypes.
   Map&lt;String, IComputer&gt; catalogue = new Map&lt;&gt;();
   // Add new prototypes to the catalogue.
   public void add(String key, IComputer prototype) {
     catalogue.put(key, prototype);
   }
   // Returns a CLONE of the specified prototype. (If it exists!)
   public IComputer get(String key) {
     if (catalogue.contains(key)) {
       return catalogue.get(key).clone();
     }
     else {
       throw new Exception("Computer doesn't exist in the catalogue!");
     }
   }
 }

Now we can populate our catalogue with useful prototypes to retrieve any time we need a clone!

static void main(String[] args) {
   ComputerCatalogue catalogue = new ComputerCatalogue();
   // Creating a prototype:
   Desktop prototypeI = new Desktop();
   prototypeI.cpu = "X12";
   prototypeI.gpu = "ix 4096";
   prototypeI.RAMigs = 128;
   // Adding it to the catalogue:
   catalogue.add("MKI", prototypeI);
   
   Desktop prototypeII = new Desktop();
   prototypeII.cpu = "Q5J";
   prototypeII.gpu = "NET1230";
   prototypeII.RAMgigs = 8;
   
   catalogue.add("MKII", prototypeII);
   // Getting clones of our prototypes:
   IComputer concreteComputerOne = (Desktop)catalogue.get("MKI");
   IComputer concreteComputerTwo = (Desktop)catalogue.get("MKII");
   // Done!
 }