Skip to content

Design Pattern: Prototype (Creational)

Copy an existing object instead of building a new one

Builds on Cloneable and clone()

Stage 3advanced2 min read

The Prototype Pattern Solution

The refactored code implements the Prototype Pattern by using Java's Cloneable interface and a Registry. Now the objects create their own copies, instead of the client calling new. Cloneable is a Java marker interface (it has no methods) that lets super.clone() copy an object's fields. WelcomeEmail.clone() makes a shallow copy, which is enough here because its fields are Strings.


1. Key Improvements

Performance via Memory Cloning

  • The Benefit: Instead of re-running expensive constructor logic (DB fetches, template parsing), the system copies the object's state in memory.
  • The Result: Creating the 1,000th email is as fast as creating the 2nd.

Decoupling (Interface-Based Creation)

  • The Benefit: The client (Main) no longer needs to know the WelcomeEmail class.
  • The Result: You can add new email types (e.g., PasswordResetEmail) to the Registry without changing any of your business logic code.

Independent State (Anti-Singleton)

  • The Benefit: Unlike a Singleton, which shares one instance, Prototype gives each user their own object, and all start from the same "blueprint."
  • The Result: Modifying the content for "User A" has zero impact on "User B."

2. Structural Breakdown

ComponentResponsibility
Prototype Interface (EmailTemplate)Declares the clone() method and the methods every template must have.
Concrete Prototype (WelcomeEmail)Implements the clone() method to return a copy of itself.
Registry (EmailTemplateRegistry)Acts as a "warehouse" for master copies. It stores initialized prototypes and provides clones to the client.
Client (Main)Requests a clone from the registry and configures the specific details needed.

3. Comparison of "Before" vs "After"

FeatureBefore (Manual new)After (Prototype Pattern)
Creation SpeedSlow (runs constructor every time).Fast (clones existing state).
DependencyClient depends on concrete classes.Client depends on the Interface and Registry.
Code DuplicationHigh (setup logic repeated in client).Low (setup logic hidden in Prototype/Registry).
ScalabilityHard to manage many variations.Easy to manage via a Registry of templates.

In this topic

After code1 file

After/Main.java100 linesGitHub
package DesignPatterns.CreationalPatterns.PrototypeDesignPattern.After;import java.util.*;/** * 1. PROTOTYPE INTERFACE * Extends Cloneable to tap into Java's native memory-copying capabilities. */interface EmailTemplate extends Cloneable {    // Returns a copy of itself. This is faster than 'new' because it    // copies the object state directly in memory.    EmailTemplate clone();    void setContent(String content);    void send(String to);}/** * 2. CONCRETE PROTOTYPE * Implements the clone logic. This class knows how to copy itself. */class WelcomeEmail implements EmailTemplate {    private String subject;    private String content;    public WelcomeEmail() {        // This heavy setup (DB calls, CSS loading) happens ONLY ONCE        // when the initial prototype is created in the Registry.        this.subject = "Welcome to TUF+";        this.content = "Hi there! Thanks for joining us.";    }    @Override    public WelcomeEmail clone() {        try {            // super.clone() performs a "Shallow Copy".            // For complex objects with nested lists, you'd perform a "Deep Copy" here.            return (WelcomeEmail) super.clone();        } catch (CloneNotSupportedException e) {            throw new RuntimeException("Clone failed", e);        }    }    @Override    public void setContent(String content) {        this.content = content;    }    @Override    public void send(String to) {        System.out.println(            "Sending to " + to + ": [" + subject + "] " + content        );    }}/** * 3. PROTOTYPE REGISTRY (Optional but Recommended) * Stores "Master Copies" of your objects. Instead of the client * knowing concrete classes, they just ask the Registry for a type. */class EmailTemplateRegistry {    private static final Map<String, EmailTemplate> templates = new HashMap<>();    static {        // Initialize the 'Master' objects once.        templates.put("welcome", new WelcomeEmail());    }    public static EmailTemplate getTemplate(String type) {        // NEVER return the original from the map.        // Always return a clone so the original stays 'pure'.        return templates.get(type).clone();    }}class Main {    public static void main(String[] args) {        // Client doesn't use 'new' or know about 'WelcomeEmail' class.        // It simply clones a pre-configured template.        EmailTemplate welcomeEmail1 = EmailTemplateRegistry.getTemplate(            "welcome"        );        welcomeEmail1.setContent("Hi Alice, welcome to TUF Premium!");        welcomeEmail1.send("alice@example.com");        EmailTemplate welcomeEmail2 = EmailTemplateRegistry.getTemplate(            "welcome"        );        welcomeEmail2.setContent("Hi Bob, thanks for joining!");        welcomeEmail2.send("bob@example.com");        /* * SUCCESS: alice@example.com and bob@example.com have independent         * objects, but we avoided the expensive setup costs of 'new'.         */    }}

Done with this topic?

How well do you know it?

PreviousDesign Pattern: Builder (Creational)