Search Knowledge

© 2026 LIBREUNI PROJECT

Software Engineering & OOAD / Design Patterns

Builder & Prototype Patterns

Builder & Prototype Patterns

In this lesson, we continue our journey through Creational Patterns by looking at the Builder and Prototype patterns. These patterns solve specific problems related to complex construction and efficient object duplication.


1. The Builder Pattern

The Problem

Imagine a Pizza class with 10+ optional parameters (size, crust, cheese, pepperoni, olives, onions, etc.). You end up with:

  1. Telescoping Constructors: A monster constructor with many arguments, many of which are often null or default.
  2. Poor Readability: new Pizza(12, true, false, true, "thin", null, ...) is impossible to read or maintain.

The Solution

The Builder pattern separates the construction of a complex object from its representation. It allows you to produce different types and representations of an object using the same construction code.

Java Example (Fluent API)

public class Pizza {
    private int size;
    private String crust;
    private boolean cheese;
    private boolean pepperoni;
    private boolean olives;

    // Private constructor - only accessible via the Builder
    private Pizza(Builder builder) {
        this.size = builder.size;
        this.crust = builder.crust;
        this.cheese = builder.cheese;
        this.pepperoni = builder.pepperoni;
        this.olives = builder.olives;
    }

    public static class Builder {
        private int size; // Required
        private String crust = "Regular"; // Default
        private boolean cheese = false;
        private boolean pepperoni = false;
        private boolean olives = false;

        public Builder(int size) { this.size = size; }

        public Builder setCrust(String crust) { this.crust = crust; return this; }
        public Builder addCheese() { this.cheese = true; return this; }
        public Builder addPepperoni() { this.pepperoni = true; return this; }
        
        public Pizza build() {
            return new Pizza(this);
        }
    }
}

// Usage
Pizza myPizza = new Pizza.Builder(12)
                    .setCrust("Thin")
                    .addCheese()
                    .addPepperoni()
                    .build();

2. The Prototype Pattern

The Problem

Creating a new object from scratch can sometimes be expensive (e.g., it requires a database query or a complex calculation). If you already have an object that is similar to what you need, it might be more efficient to simply copy it.

However, standard assignment (obj1 = obj2) only copies the reference in most languages. You need a “deep copy.”

The Solution

The Prototype pattern delegates the cloning process to the actual objects that are being cloned. The pattern declares a common interface for all objects that support cloning.

C++ Example

#include <iostream>
#include <string>
#include <memory>

class Shape {
public:
    virtual ~Shape() {}
    virtual std::unique_ptr<Shape> clone() const = 0;
    virtual void draw() const = 0;
};

class Circle : public Shape {
    int radius;
public:
    Circle(int r) : radius(r) {}
    
    // Cloning logic
    std::unique_ptr<Shape> clone() const override {
        return std::make_unique<Circle>(*this);
    }
    
    void draw() const override {
        std::cout << "Drawing Circle with radius: " << radius << std::endl;
    }
};

int main() {
    Circle prototypeCircle(10);
    
    // Instead of 'new', we clone
    auto clonedCircle = prototypeCircle.clone();
    clonedCircle->draw();
    
    return 0;
}

3. Comparison and Synergy

Builder vs. Abstract Factory

  • Builder focuses on constructing a complex object step-by-step.
  • Abstract Factory focuses on families of product objects (either simple or complex).
  • Builder returns the product as a final step, whereas the Abstract Factory returns the product immediately.

When to use Prototype?

  • When the classes to instantiate are specified at runtime.
  • When you want to avoid a hierarchy of factories.
  • When instances of a class can have one of only a few different combinations of state.

Deep Copy vs. Shallow Copy

A critical concept in the Prototype pattern is how the object is cloned:

  1. Shallow Copy: Copies the object’s top-level fields. If a field is a reference to another object, both the original and the clone will point to the same memory location.
  2. Deep Copy: Recursively copies all objects referenced by the original object. The clone is entirely independent.

In most Prototype implementations, a Deep Copy is preferred to ensure that modifying the clone does not inadvertently change the original object.


Key Takeaways

  • Use Builder when you have a “Configuration Explosion” in your constructors.
  • Use Prototype when object creation is costly and you want to reduce overhead by copying existing instances.
  • Both patterns help keep your client code decoupled from the specific implementation details of object creation.