1. Definition

The Prototype Design Pattern involves creating objects based on a template of an existing object through cloning. It allows for adding any subclass instance of a known superclass at runtime.

2. Problem Statement

Consider a scenario where instantiation of a class is expensive or complicated in some way. Perhaps it requires fetching data from a database, or the object configuration is time-consuming. Instead of re-running this expensive code every time a new instance is required, you can clone a pre-configured prototype.

3. Solution

Prototype pattern advocates the cloning process. If the cost for creating a new instance of an object is more than copying an existing instance, just clone the prototype.

4. Real-World Use Cases

1. An editor tool where shapes drawn can be cloned and replicated.

2. Game characters that can be created by duplicating existing ones with minor modifications.

3. Any scenario where objects have numerous shared configurations and only a few differences.

5. Implementation Steps

1. Create an abstract class or an interface with a clone method.

2. Concrete classes will implement the clone method.

3. A prototype registry can be introduced to get the required object prototypes.

6. Implementation

// Step 1: Create a Prototype interface with a clone method
package com.java.tutorials.prototype;

public interface Document extends Cloneable {
    Document cloneDocument();

    String showContent();
}

// Step 2: Concrete classes implementing the clone method
public class WordDocument implements Document {

    private String content;

    public WordDocument() {
        this.content = "This is a Word Document!";
    }

    @Override
    public Document cloneDocument() {
        // deep copy for actual cloning
        WordDocument clonedDoc = null;
        try {
            clonedDoc = (WordDocument) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clonedDoc;
    }

    @Override
    public String showContent() {
        return content;
    }
}

public class PdfDocument implements Document {

    private String content;

    public PdfDocument() {
        this.content = "This is a PDF Document!";
    }

    @Override
    public Document cloneDocument() {
        // deep copy for actual cloning
        PdfDocument clonedDoc = null;
        try {
            clonedDoc = (PdfDocument) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clonedDoc;
    }

    @Override
    public String showContent() {
        return content;
    }
}

// Client code to use the prototype
public class Editor {
    public static void main(String[] args) {
        Document wordDocumentPrototype = new WordDocument();
        Document clonedWordDocument = wordDocumentPrototype.cloneDocument();

        System.out.println("Original Word Document: " + wordDocumentPrototype.showContent());
        System.out.println("Cloned Word Document: " + clonedWordDocument.showContent());

        Document pdfDocumentPrototype = new PdfDocument();
        Document clonedPdfDocument = pdfDocumentPrototype.cloneDocument();

        System.out.println("Original PDF Document: " + pdfDocumentPrototype.showContent());
        System.out.println("Cloned PDF Document: " + clonedPdfDocument.showContent());
    }
}

Output:

Original Word Document: This is a Word Document!
Cloned Word Document: This is a Word Document!
Original PDF Document: This is a PDF Document!
Cloned PDF Document: This is a PDF Document!

Explanation:

In this example, we've used the Prototype pattern to create a clone of documents. Two types of documents (Word and PDF) have been created, and for each type, we're cloning the document instead of creating a new instance. This method can be beneficial if, for instance, document creation is more expensive due to some initial configuration, database calls, or other expensive computations.

7. When to use?

Use the Prototype pattern when:

1. Classes to instantiate are specified at runtime.

2. You need to hide the complexity of creating new instances.

3. Objects require one of a few shared configurations, making it more efficient to clone a pre-configured prototype rather than manually creating each instance.