Prototype Design Pattern in Java Core Java Design Patterns java Java 21 Programming by devs5003 - October 28, 2025November 1, 20251 Last Updated on November 1st, 2025Prototype Design Pattern in Java: Examples Using Java 21+ In software development, creating new objects can sometimes be expensive, especially when the object creation involves heavy database calls, network operations, or complex initialization steps. The Prototype Design Pattern helps solve this problem by allowing us to clone existing objects instead of creating new ones from scratch. Think of it like photocopying a document instead of re-typing it each time. You first create a fully configured “prototype” object, and then make copies (clones) of it whenever needed. Imagine you are designing a website for multiple clients. Each website starts with the same base template: a header, a footer, and some default sections.Instead of designing a new site from scratch every time, you can simply clone the existing template and then modify client-specific content. This is exactly how the Prototype Pattern works in code. It falls under the Creational Patterns category of the Gang of Four (GoF) design patterns. The Prototype Design Pattern is a creational design pattern that allows an object to create a copy of itself, thus avoiding the cost of creating new instances from scratch. Table of Contents Toggle Motivation / Problem StatementDefinition & IntentStructure (UML & Components)Sequence DiagramShallow vs Deep CloneImplementation: Prototype Design Pattern in Java (Modern Style)Option A: Using Cloneable carefullyOption B: Using Java record (if feasible)Option C: Use serialization / external cloningConcrete ExamplesExample: Document with nested contentExample: Caching Expensive ObjectsPrototype Registry / Prototype Factory VariantWhen to Use Prototype Pattern, Advantages, Disadvantages When to Use Prototype PatternAdvantages of Prototype PatternDisadvantages / Caveats of Prototype PatternBest Practices & Pitfalls of Prototype PatternFAQs on Prototype Design Pattern in JavaConclusionRelated Motivation / Problem Statement Imagine you have to create many complex objects in your Java program. Each object’s construction might involve: Loading data from a database Reading a file Setting up expensive computations Doing complex initialization logic If you always do new SomeComplexObject(…) and run all initialization each time, it may cost performance, resources, or time. Moreover, when many objects are similar (same configuration), it’s redundant to repeat the same setup. Instead, if you already have an existing “template” object set up, you could clone it (copy) and then adjust only what differs. That’s the core motivation for the Prototype pattern. Also, sometimes you want to be able to dynamically add new types of objects (at runtime) without modifying a factory or switch-case logic. A prototype registry + cloning can help. Thus, the Prototype pattern addresses: Performance / cost: avoid re-running expensive initialization tasks. Flexibility: allow dynamic object creation by copying an existing instance. Decoupling: calling code can refer only to a prototype interface, not concrete classes. In short: “Instead of using new for creating a new object every time and setting everything up, clone an existing one and tweak it.” According to Wikipedia on Prototype Pattern: The prototype pattern is a creational design pattern. It is used when the types of objects to create is determined by a prototypical instance, which is cloned to produce new objects. Definition & Intent Intent (GoF style): Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype. In simpler words: define a common interface for cloning (“I can reproduce myself”) and let concrete classes implement how to copy themselves. Clients don’t need to know which concrete class they’re cloning. Another way to view it: it’s a way to give objects a “virtual constructor”: The ability to instantiate new ones of their own kind without knowing their class explicitly. Key idea: the client only works with a Prototype interface (or base class) with a clone() method. Concrete prototypes override clone() to return a copy of themselves. Prototype is a creational design pattern that allows cloning objects, even complex ones, without coupling to their specific classes. The Prototype pattern is generally used when we have an instance of the class (prototype) and we’d like to create new objects by just copying the prototype. Structure (UML & Components) Here’s a simplified UML class diagram for the Prototype pattern: In words, components are: Prototype: an interface or abstract class declaring clone(). ConcretePrototypeA and ConcretePrototypeB: classes that implement Prototype and override clone(), defining the actual cloning logic. Client: code that holds or is given a Prototype, and asks it to clone. Optionally, you may add: Prototype Registry (or Factory): a central store of prototype instances, keyed by name or ID, so clients request clones by name. Support for shallow vs deep cloning: prototypes may implement different levels of copy logic. Sequence Diagram This diagram explains the Prototype pattern’s runtime behavior. The sequence diagram clearly shows the following steps: Client Reference – The Client has a reference to a prototype instance Clone Invocation – Client calls clone() on the Prototype interface Polymorphic Call – The call is dispatched to ConcretePrototype’s clone() method Instance Creation – ConcretePrototype creates a new instance (shallow or deep copy) Data Initialization – The new instance is initialized with copied data Return Path – The cloned object is returned through the interface back to the Client Usage – Client receives and uses the new cloned object Final State – Client now has two objects: the original prototype and the cloned copy Important Facts: Actor: Client initiates the cloning process Participants: Prototype (Interface), ConcretePrototype (Implementation), and Cloned Object (New Instance) Message Flow: Shows both synchronous calls and return messages Polymorphism: Demonstrates how the interface abstracts the concrete implementation Final Note: Highlights that the Client ends up with both the original and cloned objects Shallow vs Deep Clone One of the critical aspects of implementing the Prototype pattern is deciding how deep the clone should be. Shallow clone: copy the fields of the object as-is; for object references, only the references are copied (not the objects they point to). Deep clone: recursively copy referenced objects too, so the clone is completely independent of original. Which one to choose depends on whether the cloned object and original share mutable sub-objects: If all fields are primitives or immutable objects (e.g. String, Integer), shallow clone is often fine. If there are mutable fields (lists, maps, nested objects), deep clone or selective clone is safer to avoid aliasing issues. In Java, Object.clone() (via Cloneable) by default does shallow copy of fields. If you want deep clone, you must override and explicitly clone nested objects. Alternatively, we can use copy-constructors or serialization (serialize + deserialize) to perform deep copy. For example, for fields that are references, we can override clone() and manually clone those fields too. @Override public Object clone() throws CloneNotSupportedException { List<String> temp = new ArrayList<>(); for (String s : this.getEmpList()) { temp.add(s); } return new Employees(temp); } Here we cloned the internal list to create a deep copy of the list. Implementation: Prototype Design Pattern in Java (Modern Style) When implementing the Prototype pattern in Java (Java 21+), you have a few options: Using Cloneable and Object.clone() Using a copy() or duplicate() method explicitly (not Cloneable) Using serialization, or other clone utilities Using Record or copy constructors (in cases where immutability or simple data-holding makes copying easier in Java 21+) Below is a “modern style” example combining some techniques. Option A: Using Cloneable carefully public interface Prototype<T> { T clonePrototype(); } We use a generic interface rather than relying on Cloneable. public class Person implements Prototype<Person> { private String name; private int age; private Address address; // mutable nested object public Person(String name, int age, Address address) { this.name = name; this.age = age; this.address = address; } @Override public Person clonePrototype() { // shallow copy of primitives + strings is okay, but clone address deeply: Address clonedAddr = address.clone(); return new Person(this.name, this.age, clonedAddr); } // getters, setters, toString omitted... } And Address itself: public class Address { private String street; private String city; public Address(String s, String c) { this.street = s; this.city = c; } public Address clone() { return new Address(street, city); } // getters / setters } Here we avoid Cloneable and Object.clone() but manually implement deep copying. This is often safer and clearer. This approach works well in Java 21+, especially when classes are straightforward. Option B: Using Java record (if feasible) If your prototype object is mostly data, you may use a record and a copy constructor / with-pattern. For example: public record Employee(String name, int salary, Address address) { public Employee copyWithAddress(Address newAddr) { return new Employee(this.name, this.salary, newAddr); } } Then the “prototype” can be an Employee instance; clients do simple copies (records are shallow by default). For nested mutable parts you may still need to clone those separately. Java 21 also supports patterns and sealed classes, but for prototype pattern the main effort is in copying logic. Option C: Use serialization / external cloning If your object graph is complex, one option is to serialize the object (e.g. Java serialization, or JSON + deserialization, or using libraries like Kryo) to produce a deep clone. But this approach has performance overhead and is less type-safe, so use only when needed. Concrete Examples Let’s create a full example that illustrates both shallow and deep clone strategies. Example: Document with nested content Suppose you build a document editor, and you have a Document prototype. Each Document has metadata and a list of Sections, each having content. import java.util.ArrayList; import java.util.List; public class Section { private String title; private String content; public Section(String t, String c) { this.title = t; this.content = c; } public Section(Section other) { this.title = other.title; this.content = other.content; } public Section clone() { return new Section(this); } // getters/setters } public class Document implements Prototype<Document> { private String name; private List<Section> sections; public Document(String name) { this.name = name; this.sections = new ArrayList<>(); } public void addSection(Section sec) { sections.add(sec); } public List<Section> getSections() { return sections; } @Override public Document clonePrototype() { Document copy = new Document(this.name); for(Section s : this.sections) { copy.addSection(s.clone()); // deep copy of each section } return copy; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Document(").append(name).append(") with sections:\n"); for (Section s : sections) { sb.append(" ").append(s.title).append(": ").append(s.content).append("\n"); } return sb.toString(); } } And client usage: public class ClientApp { public static void main(String[] args) { Document prototype = new Document("TemplateDoc"); prototype.addSection(new Section("Intro", "This is intro")); prototype.addSection(new Section("Body", "Body content")); // Now clone it: Document userDoc = prototype.clonePrototype(); userDoc.addSection(new Section("Conclusion", "User’s own end")); System.out.println("Prototype:\n" + prototype); System.out.println("UserDoc (cloned + modified):\n" + userDoc); } } What to observe: We deep cloned each Section. Thus changes in userDoc’s sections won’t affect prototype’s sections. We didn’t rely on Cloneable or Object.clone(). This approach is clearer and safer for many use cases Example: Caching Expensive Objects Suppose you have a heavy object: ConfigModel that loads from DB or file. You want to build clones rapidly. public class ConfigModel implements Prototype<ConfigModel> { private String name; private Map<String, String> settings; public ConfigModel(String name, Map<String, String> settings) { this.name = name; this.settings = new HashMap<>(settings); } @Override public ConfigModel clonePrototype() { // deep copy map Map<String, String> newMap = new HashMap<>(this.settings); return new ConfigModel(this.name, newMap); } // getters, setters, toString } You can keep one “master” ConfigModel loaded once, and then clone whenever you need a fresh modifiable copy. Prototype Registry / Prototype Factory Variant Often you don’t want client code to hold directly to prototype instances. Instead, you manage a registry (map of names to prototype instances). Clients ask the registry for a clone by name. This adds flexibility. public class PrototypeRegistry { private static final Map<String, Prototype<?>> registry = new HashMap<>(); public static <T extends Prototype<T>> void register(String key, T prototype) { registry.put(key, prototype); } @SuppressWarnings("unchecked") public static <T extends Prototype<T>> T getClone(String key) { T proto = (T) registry.get(key); if (proto == null) { throw new IllegalArgumentException("No prototype registered for key " + key); } return proto.clonePrototype(); } } Usage: // during initialization PrototypeRegistry.register("doc", prototype); PrototypeRegistry.register("config", configModelPrototype); // later in client Document clonedDoc = PrototypeRegistry.getClone("doc"); ConfigModel clonedCfg = PrototypeRegistry.getClone("config"); This variant blends factory + prototype ideas: clients request by name, but new object is produced by cloning. This approach avoids modifying client code when you add new prototype types – just register a new prototype instance. When to Use Prototype Pattern, Advantages, Disadvantages When to Use Prototype Pattern Object creation is expensive (initialization cost, I/O, DB, complex structure). Many similar instances are needed that only differ slightly. You want to avoid stuffing of many constructors with many parameters. You want to decouple client code from concrete classes. You want dynamic runtime flexibility: e.g. new prototype types can be plugged in without changing existing logic. Advantages of Prototype Pattern Performance gain: avoids repeating heavy setup. Dynamic extensibility: new prototypes can be added (especially via registry) without modifying client logic. Simplified object creation: clients don’t need to know construction logic or class names. Fewer subclasses / combinatorial explosion avoided: instead of many subclass variants, you clone and alter. Disadvantages / Caveats of Prototype Pattern Clone complexity: deep cloning logic can become tricky and error-prone. Hidden costs: some clones (especially deep) may still cost time/memory. Object identity: you must be careful not to confuse original vs clones. Prototype lifecycle management: what happens if prototypes hold resources (DB connections, open files)? Cloning may not be appropriate. Copy semantics ambiguity: shallow vs deep decisions may introduce subtle bugs. Serialization cloning trade-offs: slower, less safe, but sometimes necessary. Best Practices & Pitfalls of Prototype Pattern Prefer explicit copy methods (copy constructor or clonePrototype()) over Object.clone() in most contexts. Be cautious with mutable internal fields (lists, maps, nested objects). Always decide deep or shallow. Avoid using super.clone() unless you understand how it behaves for your class hierarchy. Use final / immutable fields whenever possible to reduce cloning complexity. Document whether clones are shallow or deep. If using a registry, ensure thread safety when registering or retrieving clones. Avoid clones that carry heavy external resources (DB connections, file handles), unless those resources are also managed properly. In Java 21+, consider using records or builder patterns in combination with prototypes for simple data objects. Write unit tests to ensure that modifications on clones don’t affect originals (especially for nested state). FAQs on Prototype Design Pattern in Java What is the main purpose of the Prototype Design Pattern? The purpose is to create new objects by cloning existing instances rather than constructing them from scratch, thus saving time and system resources. How is Prototype Pattern different from Factory Pattern? Factory Pattern creates new instances using constructors. Prototype Pattern creates new instances by copying existing ones. Factories decide which type to create, while prototypes decide how to duplicate an existing type. What are shallow and deep cloning in Java? Shallow Clone: Copies only the top-level object. References inside the object still point to the same memory. Deep Clone: Copies the entire object graph, creating independent copies of nested objects. How can we implement cloning in Java? There are several approaches: Implement the Cloneable interface and override clone() method. Create a copy constructor. Use serialization or JSON mapping for deep cloning. Implement a custom Prototype interface. What is a Prototype Registry? A Prototype Registry is a centralized storage (usually a Map) where all prototype objects are registered with unique keys.It helps you retrieve and clone prototypes easily. Example: PrototypeRegistry.register("invoice", invoiceTemplate); Document clone = PrototypeRegistry.getClone("invoice"); Is the Cloneable interface recommended in modern Java? Not necessarily. The Cloneable interface in Java is considered flawed because: It does not define the clone() method. It breaks encapsulation by performing shallow copies by default. In modern Java (Java 17–21), developers prefer copy constructors, factory methods, or records for immutable cloning. Can we use Prototype Pattern with immutable objects? Not effectively. The Prototype Pattern makes sense only when objects are mutable, since immutable objects can be reused directly without cloning. What are some real-world use cases of the Prototype Pattern? Document templates (Word, PDF, or reports) UI widgets or configuration models Game characters or shapes with similar base properties Machine learning models (cloning pre-trained model configurations) What is the role of the Prototype interface in this pattern? The interface defines a generic method (like clonePrototype()) that each concrete class must implement to create its own copy. This ensures a consistent cloning contract across different object types. How can we test if the clone is deep or shallow? We can verify it by modifying a nested object in the clone and checking whether the original object changes. If it changes, it’s a shallow clone; if not, it’s a deep clone. Can Prototype Pattern and Factory Pattern be used together? Yes. Factories can create prototypes, and prototypes can be registered in a Prototype Registry. Later, the system can use those prototypes to quickly clone new instances instead of calling factories again. Conclusion The Prototype Design Pattern is a creational pattern that formalizes object cloning: new objects are created by copying an existing prototype rather than by calling a constructor. It helps improve performance, flexibility, and decoupling of client code from concrete classes. Use a Prototype interface or abstraction with a clone() / clonePrototype() method. Concrete prototypes implement the clone logic (shallow or deep). Clients simply call clone(), not new. Optionally use a registry / prototype factory to manage prototype instances centrally. Be careful with deep vs shallow copying and mutable internal state. In modern Java, prefer explicit copy methods over Cloneable unless you have good reason. When used thoughtfully, the Prototype pattern can reduce boilerplate, improve runtime flexibility, and simplify object creation logic, especially for classes whose instances are costly to build from scratch. Ready to learn about another creational pattern? Let’s visit, the Abstract Factory Pattern in Java with all scenarios explained. You may also check, the hub page of Java Design Patterns. Related