Implementing Creational Design Patterns

1. Implementing Singleton Pattern

VariantTrait
EagerCreated at class load
LazyCreated on first use
EnumJoshua Bloch recommended; serialization-safe
DI-managedSpring @Component default scope

Example: Enum singleton

public enum Config {
    INSTANCE;
    private final Properties props = load();
    public String get(String key) { return props.getProperty(key); }
}
Warning: Singletons are global state — prefer DI-managed singletons for testability.

2. Implementing Factory Method Pattern

Example: Subclass decides type

abstract class Dialog {
    abstract Button createButton();
    void render() { createButton().draw(); }
}
class WindowsDialog extends Dialog {
    Button createButton() { return new WindowsButton(); }
}

3. Implementing Abstract Factory Pattern

Example: Family of products

interface UiFactory {
    Button createButton();
    Checkbox createCheckbox();
}
class MacFactory implements UiFactory { ... }
class WinFactory implements UiFactory { ... }
Factory MethodAbstract Factory
One productFamily of related products
InheritanceComposition

4. Implementing Builder Pattern

Example: Fluent builder

public final class HttpRequest {
    private final String url; private final String method; private final Map<String,String> headers;
    private HttpRequest(Builder b) { url=b.url; method=b.method; headers=Map.copyOf(b.headers); }
    public static Builder builder() { return new Builder(); }
    public static class Builder {
        private String url, method = "GET";
        private final Map<String,String> headers = new HashMap<>();
        public Builder url(String u) { this.url = u; return this; }
        public Builder header(String k, String v) { headers.put(k,v); return this; }
        public HttpRequest build() {
            Objects.requireNonNull(url, "url");
            return new HttpRequest(this);
        }
    }
}

5. Implementing Prototype Pattern

StepJava Mechanism
Clone interfaceCustom copy() method (avoid Cloneable)
Deep vs shallowRecursively copy mutable fields
RegistryMap of name → prototype

6. Implementing Factory Pattern

Example: Simple factory

public final class ShapeFactory {
    public static Shape create(String type) {
        return switch (type) {
            case "circle"  -> new Circle();
            case "square"  -> new Square();
            default -> throw new IllegalArgumentException(type);
        };
    }
}

7. Implementing Simple Factory

AspectDetail
Not a GoF patternIdiom; central creation method
Use whenFew types, stable hierarchy
LimitationAdding type means modifying factory (OCP cost)

8. Using Static Factory Methods

MethodConvention
ofList.of(1,2)
fromInstant.from(temporal)
valueOfInteger.valueOf("1")
getInstanceCalendar.getInstance()
newInstanceNew each call

9. Choosing Between Factory Patterns

NeedPattern
Single type, named creationStatic factory method
Many params/optionalBuilder
Subclass-decided typeFactory Method
Family of productsAbstract Factory

10. Implementing Object Pool Pattern

Use CaseExample
Expensive creationDB connections (HikariCP)
Limited resourcesThreads (ThreadPoolExecutor)
ReuseBuffer pools

11. Implementing Registry Pattern

Example: Type registry

public class HandlerRegistry {
    private final Map<String, Handler> handlers = new ConcurrentHashMap<>();
    public void register(String key, Handler h) { handlers.put(key, h); }
    public Handler resolve(String key) {
        return Optional.ofNullable(handlers.get(key))
            .orElseThrow(() -> new NoSuchElementException(key));
    }
}

12. Implementing Multiton Pattern

AspectDetail
GoalBounded set of instances by key
StorageConcurrentHashMap<Key, Instance>
CreationcomputeIfAbsent for thread safety

13. Implementing Dependency Injection Pattern

FormProsCons
ConstructorRequired deps explicit, immutableMany params
SetterOptional depsMutability
FieldConciseHidden deps, hard to test DISCOURAGED

14. Understanding Dependency Injection

Without DI:  ServiceA ──new── ServiceB  (tight)
With DI:     ServiceA ─uses─→ IServiceB ←─impl── ServiceB
             Container injects ServiceB into ServiceA
  
BenefitDetail
TestabilityInject mocks
FlexibilitySwap implementations
SRPClass focuses on its job, not wiring

15. Using Service Locator Pattern

vs DILocatorDI
DiscoveryPulls from locatorPushed in
VisibilityHidden depsExplicit
TestabilityHarder (global)Easier
Warning: Service Locator is often considered an anti-pattern — prefer constructor DI.

16. Understanding IoC Containers

ContainerEcosystem
SpringJava, JVM
GuiceJava, lightweight
DaggerCompile-time DI (Android, Java)
Micronaut/QuarkusCompile-time, fast startup
CDIJakarta EE standard

17. Using Service Containers

ResponsibilityDetail
Bean instantiationReflection or compile-time
WiringResolve constructor params
ScopeSingleton, prototype, request, session
Lifecycleinit/destroy callbacks

18. Understanding Thread-Safe Singleton

ApproachTrade-off
Eager initSimple, no lazy benefit
synchronized getterSafe but slow
Double-checked lockingFast; needs volatile
Holder idiomBest balance
EnumSimplest, serialization-safe

19. Using Double-Checked Locking

Example: Correct DCL

public class Lazy {
    private static volatile Lazy instance;
    public static Lazy get() {
        Lazy local = instance;
        if (local == null) {
            synchronized (Lazy.class) {
                local = instance;
                if (local == null) instance = local = new Lazy();
            }
        }
        return local;
    }
}

20. Using Initialization-on-Demand Holder

Example: Holder idiom

public class Singleton {
    private Singleton() {}
    private static class Holder { static final Singleton INSTANCE = new Singleton(); }
    public static Singleton get() { return Holder.INSTANCE; }
}
Note: JVM guarantees class initialization is thread-safe and lazy.

21. Implementing Lazy Initialization

ToolUse
Supplier<T>Defer creation
Holder classClass-level lazy
Suppliers.memoizeGuava cached supplier

22. Using Lazy<T> for Lazy Initialization

Example: Memoized supplier

public final class Lazy<T> {
    private final Supplier<T> init;
    private volatile T value;
    public Lazy(Supplier<T> init) { this.init = init; }
    public T get() {
        T v = value;
        if (v == null) synchronized (this) { if (value == null) value = v = init.get(); }
        return v;
    }
}

23. Using Functional Options

Example: Configurer pattern

HttpClient client = HttpClient.create(o -> o
    .timeout(Duration.ofSeconds(5))
    .retries(3)
    .followRedirects(true));

24. Using Structs for Simple Factories

Java EquivalentDetail
RecordConcise immutable carrier
Static factoryPoint.of(x,y) on the record

25. Using Class Methods as Factories

Example: Named static creators

public final class Money {
    public static Money usd(long cents) { return new Money(cents, USD); }
    public static Money zero(Currency c) { return new Money(0, c); }
    private Money(long cents, Currency c) { ... }
}

26. Using Companion Objects for Factories

LanguageMechanism
Kotlincompanion object { fun create() = ... }
ScalaObject with same name as class
Java equivalentStatic factory methods

27. Using Module Mixins for Object Creation

LanguageMechanism
Rubyinclude Module with self.create
Java equivalentDefault methods on factory interfaces