GoF Design Patterns in Java: Singleton, Factory, Builder & Observer

Design Patterns are proven, time-tested architectural templates for solving recurring software design challenges. Originating from the famous Gang of Four (GoF) book, mastering these patterns is a mandatory benchmark for technical interviews, BCA/MCA university syllabi, and building production-grade enterprise software.

The Three Categories of GoF Design Patterns

  • Creational Patterns: Focus on safe, flexible object instantiation (Singleton, Factory, Builder, Prototype).
  • Structural Patterns: Focus on assembling classes and objects into larger, flexible structures (Adapter, Decorator, Facade, Composite).
  • Behavioral Patterns: Focus on efficient communication, algorithms, and delegation of responsibilities between objects (Observer, Strategy, Command, Iterator).

1. Singleton Pattern (Thread-Safe & Reflection-Proof)

Guarantees that a class has only one single instance throughout the JVM lifecycle and provides a global access point.

Approach A: Bill Pugh Static Inner Class (Lazy & Thread-Safe)

public class DatabasePool {
    // 1. Private constructor prevents instantiation via 'new'
    private DatabasePool() {
        System.out.println("Initializing Database Connection Pool...");
    }

    // 2. Static inner helper class (Loaded only when getInstance() is called)
    private static class PoolHolder {
        private static final DatabasePool INSTANCE = new DatabasePool();
    }

    // 3. Global access point
    public static DatabasePool getInstance() {
        return PoolHolder.INSTANCE;
    }
}

Approach B: Enum Singleton (100% Reflection & Serialization Proof)

public enum AppConfig {
    INSTANCE;
    
    private String serverUrl = "https://api.veewom.com";
    
    public String getServerUrl() { return serverUrl; }
    public void setServerUrl(String url) { this.serverUrl = url; }
}

2. Factory Method Pattern

Defines an interface for creating objects, letting subclasses decide which concrete class to instantiate without exposing instantiation logic to the client.

// Common Interface
interface Notification {
    void notifyUser();
}

// Concrete Implementations
class SMSNotification implements Notification {
    public void notifyUser() { System.out.println("Sending SMS alert via Twilio..."); }
}

class EmailNotification implements Notification {
    public void notifyUser() { System.out.println("Sending Email via Amazon SES..."); }
}

// Factory
class NotificationFactory {
    public static Notification createNotification(String channel) {
        if (channel == null || channel.isEmpty()) return null;
        switch (channel.toUpperCase()) {
            case "SMS": return new SMSNotification();
            case "EMAIL": return new EmailNotification();
            default: throw new IllegalArgumentException("Unknown channel: " + channel);
        }
    }
}

public class FactoryDemo {
    public static void main(String[] args) {
        Notification note = NotificationFactory.createNotification("SMS");
        note.notifyUser();
    }
}

3. Builder Pattern (Fluent & Immutable)

Solves the anti-pattern of "telescoping constructors" when an object has many attributes (some mandatory, some optional):

public class Server {
    // Mandatory fields
    private final String hostname;
    private final int port;
    
    // Optional fields
    private final boolean sslEnabled;
    private final int maxConnections;

    private Server(Builder builder) {
        this.hostname = builder.hostname;
        this.port = builder.port;
        this.sslEnabled = builder.sslEnabled;
        this.maxConnections = builder.maxConnections;
    }

    public static class Builder {
        private final String hostname;
        private final int port;
        private boolean sslEnabled = false;
        private int maxConnections = 100;

        public Builder(String hostname, int port) {
            this.hostname = hostname;
            this.port = port;
        }

        public Builder enableSsl(boolean enable) {
            this.sslEnabled = enable;
            return this;
        }

        public Builder maxConnections(int max) {
            this.maxConnections = max;
            return this;
        }

        public Server build() {
            return new Server(this);
        }
    }

    @Override
    public String toString() {
        return "Server[" + hostname + ":" + port + ", SSL=" + sslEnabled + ", Max=" + maxConnections + "]";
    }
}

public class BuilderDemo {
    public static void main(String[] args) {
        Server prodServer = new Server.Builder("veewom.com", 443)
                                  .enableSsl(true)
                                  .maxConnections(5000)
                                  .build();
                                  
        System.out.println(prodServer);
    }
}

4. Observer Pattern (Event-Driven Publish/Subscribe)

Defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified automatically:

import java.util.ArrayList;
import java.util.List;

// Observer Interface
interface StockSubscriber {
    void onPriceUpdate(String stockSymbol, double newPrice);
}

// Subject / Publisher
class StockExchange {
    private final List subscribers = new ArrayList<>();

    public void register(StockSubscriber sub) { subscribers.add(sub); }
    public void unregister(StockSubscriber sub) { subscribers.remove(sub); }

    public void setPrice(String symbol, double price) {
        for (StockSubscriber sub : subscribers) {
            sub.onPriceUpdate(symbol, price);
        }
    }
}

public class ObserverDemo {
    public static void main(String[] args) {
        StockExchange bse = new StockExchange();
        
        // Lambda expressions as observers!
        bse.register((symbol, price) -> System.out.printf("Trader App 1: %s is now $%.2f%n", symbol, price));
        bse.register((symbol, price) -> System.out.printf("SMS Notification: Alert! %s at $%.2f%n", symbol, price));

        bse.setPrice("GOOGL", 182.50);
    }
}
BCA / University Exam Corner
  • Viva Question: Give examples of Design Patterns already present in standard Java JDK libraries.
    Answer:
    • Singleton: Runtime.getRuntime(), Desktop.getDesktop()
    • Builder: StringBuilder.append(), java.net.http.HttpRequest.newBuilder()
    • Factory: Calendar.getInstance(), NumberFormat.getInstance()
    • Decorator: new BufferedReader(new FileReader("file.txt"))
    • Adapter: Arrays.asList()
    • Strategy: Collections.sort(list, Comparator)

Frequently Asked Questions (FAQ)

Q1. What are Design Patterns in software engineering?
Design Patterns are battle-tested, reusable architectural solutions to commonly occurring software design problems. Formalized by the Gang of Four (GoF), they are divided into Creational (object creation mechanisms), Structural (class and object composition), and Behavioral (communication and responsibility assignment between objects).
Q2. How do you create a 100% thread-safe Singleton in modern Java?
The recommended modern Java approach is using an Enum Singleton (Joshua Bloch pattern), which is inherently thread-safe, protected against reflection tampering, and guarantees serialization safety. Alternatively, Bill Pugh Initialization-on-demand Holder Pattern or Double-Checked Locking with a volatile instance variable are widely used.
Q3. When should you use the Builder Pattern instead of multiple constructors?
When a class has more than 4 or 5 parameters, especially when many are optional. Telescoping constructors become unreadable and error-prone. The Builder pattern provides a fluent, step-by-step API, ensures immutability, and makes code self-documenting.

Explore Related Java Tutorials

Accelerate your Java mastery by studying these tightly connected core tutorials:

OOP Principles →

Encapsulation, Polymorphism, and Abstraction foundations.

Java Multithreading →

Thread safety and volatile in Singletons.

Java Records →

Immutable data carriers and builder synergy.

Collections Framework →

Iterator and Decorator patterns in standard Java.

Share Share on Facebook Share on Twitter Share on LinkedIn Pin on Pinterest Share on Stumbleupon Share on Tumblr Share on Reddit Share on Diggit

You may also like this!