Norbu Tshering Lepcha
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
    • Invite by email
      Invitee

      This note has no invitees

    • Publish Note

      Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

      Your note will be visible on your profile and discoverable by anyone.
      Your note is now live.
      This note is visible on your profile and discoverable online.
      Everyone on the web can find and read all notes of this public team.

      Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

      Explore these features while you wait
      Complete general settings
      Bookmark and like published notes
      Write a few more notes
      Complete general settings
      Write a few more notes
      See published notes
      Unpublish note
      Please check the box to agree to the Community Guidelines.
      View profile
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Note Insights New
    • Make a copy
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Note Insights Versions and GitHub Sync Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Make a copy Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
  • Invite by email
    Invitee

    This note has no invitees

  • Publish Note

    Share your work with the world Congratulations! 🎉 Your note is out in the world Publish Note No publishing access yet

    Your note will be visible on your profile and discoverable by anyone.
    Your note is now live.
    This note is visible on your profile and discoverable online.
    Everyone on the web can find and read all notes of this public team.

    Your account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Your team account was recently created. Publishing will be available soon, allowing you to share notes on your public page and in search results.

    Explore these features while you wait
    Complete general settings
    Bookmark and like published notes
    Write a few more notes
    Complete general settings
    Write a few more notes
    See published notes
    Unpublish note
    Please check the box to agree to the Community Guidelines.
    View profile
    Engagement control
    Commenting
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    • Everyone
    Suggest edit
    Permission
    Disabled Forbidden Owners Signed-in users Everyone
    Enable
    Permission
    • Forbidden
    • Owners
    • Signed-in users
    Emoji Reply
    Enable
    Import from Dropbox Google Drive Gist Clipboard
       Owned this note    Owned this note      
    Published Linked with GitHub
    • Any changes
      Be notified of any changes
    • Mention me
      Be notified of mention me
    • Unsubscribe
    # Creational Design Patterns ## Definition Creational design patterns are a category of object-oriented design patterns that ==***abstract and encapsulate the process of object creation***==. Rather than instantiating objects directly using the `new` keyword (or its language equivalent), creational patterns provide controlled, flexible, and reusable mechanisms for constructing objects. In modern software systems, object creation is rarely as simple as calling a constructor. Real-world systems must decide: - Which concrete class to instantiate at runtime - How to configure complex objects with many dependencies - How to ensure only one instance exists across an entire application - How to construct families of related objects - How to clone or replicate existing object state efficiently Creational patterns address these concerns by separating the *what* (the object being created) from the *how* (the creation logic). This separation is foundational to building loosely coupled, testable, and maintainable software systems. ## Significance In small programs, calling `new SomeClass()` works fine. In large-scale systems, however, direct instantiation creates several severe problems: - **Tight coupling**: Code that instantiates concrete classes directly cannot easily switch implementations without modification. - **Difficulty in testing**: Hard-coded dependencies cannot be replaced with mocks or stubs. - **Configuration complexity**: Production, staging, and test environments often need different object configurations. - **Resource management issues**: Some objects (database connections, configuration managers, loggers) must be shared, not duplicated. - **Construction complexity**: Objects with many optional parameters lead to unreadable constructors. Creational patterns are widely used in: - Enterprise application frameworks (Spring, .NET, Hibernate) - Game engines (entity factories, prefab cloning) - Cloud infrastructure SDKs (AWS SDK builders, Kubernetes client factories) - ORM libraries (entity instantiation, lazy loading) - Dependency injection containers (Spring, Guice, Dagger, ASP.NET Core DI) ## Intuition Think of an architect designing a large residential complex. The architect does not personally lay every brick. Instead, the architect designs blueprints, hires specialists, and delegates construction to teams. Each team knows how to build a particular component, kitchens, plumbing, electrical systems, without the architect micromanaging every detail. Creational patterns work the same way. The code that *uses* an object should not be burdened with the knowledge of *how* to build that object. Construction logic is delegated to specialized constructs factories, builders, prototypes, or injection containers that hide complexity behind a clean interface. Another useful mental model: object creation is like ordering food at a restaurant. You do not enter the kitchen and prepare the meal yourself. You declare what you want, and the kitchen handles ingredients, cooking, and plating. Creational patterns are the restaurant kitchens of software construction. ## Core Concepts and Principles Creational patterns are governed by several object-oriented design principles: **Encapsulation of creation logic** — The details of object construction should be hidden from the client code that uses the object. The client should depend on abstractions, not concrete implementations. **Program to an interface, not an implementation** — Clients should hold references to interfaces or abstract base classes. The actual concrete type is chosen by the creational mechanism, allowing future substitution. **Single Responsibility Principle (SRP)** — Object creation is a responsibility distinct from object behavior. Mixing the two violates SRP and produces fragile code. **Open/Closed Principle (OCP)** — Creational patterns make it possible to introduce new object types without modifying existing client code. **Dependency Inversion Principle (DIP)** — High-level modules should not depend on low-level modules; both should depend on abstractions. Creational patterns enforce this discipline. **Common trade-offs** include: - Added indirection and complexity versus flexibility gained - Increased number of classes versus cleaner separation of concerns - Runtime configuration cost versus static instantiation speed - Hidden construction logic versus explicit, easy-to-read code **Common mistakes** include: - Overusing patterns where simple instantiation would suffice - Using Singleton as a global variable, polluting the global state - Creating deeply nested factory hierarchies that obscure intent - Hard-coding concrete factories instead of injecting them ## Architecture Components While creational patterns operate at the class level rather than the system level, they manifest in larger architectures through several recurring components. **Client** — The code that needs a constructed object. It interacts only with abstractions and creational interfaces, never with concrete construction logic. **Product** — The object being created. Often an interface or abstract class with multiple concrete implementations. **Creator (Factory)** — The component responsible for producing Products. It encapsulates the decision of which concrete class to instantiate. **Builder** — A specialized creator used when construction involves many steps or optional parameters. **Prototype Registry** — A store of pre-configured objects that can be cloned on demand. **Dependency Injection Container** — An infrastructure-level component that resolves and injects dependencies automatically based on configuration metadata. **Configuration Source** — External files (YAML, JSON, environment variables) or annotations that drive what objects get created and how. **Lifecycle Manager** — Manages object lifetimes: singleton, transient, scoped (per request, per session). ## Workflow and Request Lifecycle Consider a typical web request in a modern backend service that uses creational patterns extensively: 1. A request enters the application server. 2. The framework's DI container resolves the controller, instantiating it with its required services (repositories, validators, mappers). 3. Each service may itself depend on further objects — database connections, HTTP clients, caches — which the container constructs or retrieves from a singleton registry. 4. Inside business logic, complex domain objects are built using Builder patterns for clarity. 5. When the system needs a polymorphic component (e.g., a payment gateway), a Factory Method selects the correct implementation based on configuration. 6. Some heavyweight objects are cloned from prototypes rather than constructed from scratch. 7. After the request completes, transient objects are garbage collected; singletons persist. This workflow shows that creational patterns are not isolated curiosities — they are woven into the request lifecycle of nearly every non-trivial backend system. ## Algorithms, Design Techniques, and Patterns This section presents each of the six creational patterns in depth. ### Singleton **Definition**: Ensures a class has only one instance and provides a global access point to it. **Intuition**: There is only one operating system kernel, one logging facility, one configuration registry. Some resources are inherently single-instance, and constructing them multiple times causes bugs or wastes resources. **Structure**: ```mermaid classDiagram class Singleton { -static instance: Singleton -Singleton() +static getInstance() Singleton +businessMethod() } Singleton --> Singleton : returns ``` **Implementation in Java (thread-safe with double-checked locking)**: ```java public final class ConfigurationManager { private static volatile ConfigurationManager instance; private final Properties props; private ConfigurationManager() { props = loadConfiguration(); } public static ConfigurationManager getInstance() { if (instance == null) { synchronized (ConfigurationManager.class) { if (instance == null) { instance = new ConfigurationManager(); } } } return instance; } public String get(String key) { return props.getProperty(key); } private Properties loadConfiguration() { // Load from file, env vars, etc. return new Properties(); } } ``` **Implementation in Python (using metaclass)**: ```python class SingletonMeta(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super().__call__(*args, **kwargs) return cls._instances[cls] class Logger(metaclass=SingletonMeta): def __init__(self): self.history = [] def log(self, message: str) -> None: self.history.append(message) print(f"[LOG] {message}") ``` **Implementation in C++ (Meyers Singleton, thread-safe since C++11)**: ```cpp class DatabaseConnection { public: static DatabaseConnection& getInstance() { static DatabaseConnection instance; return instance; } DatabaseConnection(const DatabaseConnection&) = delete; DatabaseConnection& operator=(const DatabaseConnection&) = delete; void executeQuery(const std::string& sql) { /* ... */ } private: DatabaseConnection() { /* connect */ } }; ``` **Use cases**: Logging frameworks, configuration managers, connection pools, hardware interface abstractions, caches, registries. **Trade-offs**: - Introduces hidden global state — a major source of testing difficulty. - Tightly couples consumers to a specific class, violating DIP. - Concurrency is non-trivial; naive implementations have race conditions. - Modern practice often replaces Singleton with a DI-managed singleton-scoped service. Singleton is the most overused and most misused creational pattern. In modern systems, prefer dependency-injected singleton-scoped services over hand-rolled Singletons. ### Factory Method **Definition**: Defines an interface for creating an object but lets subclasses decide which concrete class to instantiate. **Intuition**: A logistics company has many transport modes — trucks, ships, planes. The general workflow of "deliver a package" is the same, but the *vehicle* used depends on context. The Factory Method delegates the choice of vehicle to specialized subclasses. **Structure**: ```mermaid classDiagram class Creator { +factoryMethod()* Product +operation() } class ConcreteCreatorA { +factoryMethod() Product } class ConcreteCreatorB { +factoryMethod() Product } class Product { <<interface>> } class ConcreteProductA class ConcreteProductB Creator <|-- ConcreteCreatorA Creator <|-- ConcreteCreatorB Product <|.. ConcreteProductA Product <|.. ConcreteProductB ConcreteCreatorA ..> ConcreteProductA ConcreteCreatorB ..> ConcreteProductB ``` **Implementation in Java**: ```java public interface Notification { void send(String message); } public class EmailNotification implements Notification { public void send(String message) { /* SMTP */ } } public class SMSNotification implements Notification { public void send(String message) { /* Twilio API */ } } public abstract class NotificationCreator { public abstract Notification createNotification(); public void notifyUser(String message) { Notification n = createNotification(); n.send(message); } } public class EmailCreator extends NotificationCreator { public Notification createNotification() { return new EmailNotification(); } } public class SMSCreator extends NotificationCreator { public Notification createNotification() { return new SMSNotification(); } } ``` **Use cases**: Plugin systems, document parsers, payment processors, notification dispatchers, database driver selection. **Trade-offs**: - Adds a parallel hierarchy of creator classes. - Improves extensibility — new product types require only a new subclass. - Especially useful when the creation logic is non-trivial and varies by context. ### Abstract Factory **Definition**: Provides an interface for creating *families* of related or dependent objects without specifying their concrete classes. **Intuition**: Imagine designing a cross-platform UI library. On Windows, you need Windows-style buttons, scrollbars, and dialogs. On macOS, you need macOS-style equivalents. Abstract Factory ensures that whichever platform you target, you get a consistent family of widgets — never a Windows button accidentally placed in a macOS dialog. **Structure**: ```mermaid classDiagram class AbstractFactory { <<interface>> +createButton() Button +createCheckbox() Checkbox } class WindowsFactory { +createButton() WindowsButton +createCheckbox() WindowsCheckbox } class MacFactory { +createButton() MacButton +createCheckbox() MacCheckbox } class Button { <<interface>> } class Checkbox { <<interface>> } AbstractFactory <|.. WindowsFactory AbstractFactory <|.. MacFactory Button <|.. WindowsButton Button <|.. MacButton Checkbox <|.. WindowsCheckbox Checkbox <|.. MacCheckbox ``` **Implementation in Python**: ```python from abc import ABC, abstractmethod class Button(ABC): @abstractmethod def render(self): pass class Checkbox(ABC): @abstractmethod def render(self): pass class WindowsButton(Button): def render(self): print("Rendering Windows button") class WindowsCheckbox(Checkbox): def render(self): print("Rendering Windows checkbox") class MacButton(Button): def render(self): print("Rendering Mac button") class MacCheckbox(Checkbox): def render(self): print("Rendering Mac checkbox") class GUIFactory(ABC): @abstractmethod def create_button(self) -> Button: pass @abstractmethod def create_checkbox(self) -> Checkbox: pass class WindowsFactory(GUIFactory): def create_button(self): return WindowsButton() def create_checkbox(self): return WindowsCheckbox() class MacFactory(GUIFactory): def create_button(self): return MacButton() def create_checkbox(self): return MacCheckbox() def render_ui(factory: GUIFactory): factory.create_button().render() factory.create_checkbox().render() ``` **Use cases**: Cross-platform UI toolkits, multi-cloud SDKs (AWS vs Azure resource builders), themed component libraries, database driver families. **Trade-offs**: - Strongly enforces consistency across product families. - Adding a new *product type* (e.g., a Slider) requires updating every factory — violates OCP in that dimension. - Adding a new *family* (e.g., Linux UI) is straightforward. ### Builder **Definition**: Separates the construction of a complex object from its representation, allowing the same construction process to create different representations. **Intuition**: Constructing a deeply customizable object — like a SQL query, an HTTP request, or a meal at a build-your-own restaurant — using a single constructor with 15 parameters is painful and error-prone. The Builder pattern offers a fluent, step-by-step API. **Structure**: ```mermaid classDiagram class Director { -builder: Builder +construct() } class Builder { <<interface>> +reset() +setPartA() +setPartB() +getResult() Product } class ConcreteBuilder { -product: Product +reset() +setPartA() +setPartB() +getResult() Product } class Product Director o--> Builder Builder <|.. ConcreteBuilder ConcreteBuilder ..> Product ``` **Implementation in Java (fluent builder)**: ```java public final class HttpRequest { private final String url; private final String method; private final Map<String, String> headers; private final String body; private final int timeoutMs; private HttpRequest(Builder b) { this.url = b.url; this.method = b.method; this.headers = b.headers; this.body = b.body; this.timeoutMs = b.timeoutMs; } public static Builder newBuilder() { return new Builder(); } public static final class Builder { private String url; private String method = "GET"; private Map<String, String> headers = new HashMap<>(); private String body = ""; private int timeoutMs = 30000; public Builder url(String url) { this.url = url; return this; } public Builder method(String m) { this.method = m; return this; } public Builder header(String k, String v) { headers.put(k, v); return this; } public Builder body(String b) { this.body = b; return this; } public Builder timeout(int ms) { this.timeoutMs = ms; return this; } public HttpRequest build() { if (url == null) throw new IllegalStateException("URL required"); return new HttpRequest(this); } } } // Usage HttpRequest req = HttpRequest.newBuilder() .url("https://api.example.com/users") .method("POST") .header("Content-Type", "application/json") .body("{\"name\":\"Alice\"}") .timeout(5000) .build(); ``` **Use cases**: HTTP clients (OkHttp, Apache HttpClient), SQL query builders (jOOQ, QueryDSL), test data builders, configuration objects, AWS SDK request builders. **Trade-offs**: - Excellent readability for objects with many fields. - Enforces immutability of the built product. - Adds boilerplate; mitigated by language features (Lombok `@Builder`, Kotlin data classes). !!! tip When a constructor has more than four parameters, especially with several optional ones, the Builder pattern almost always improves code clarity. ### Prototype **Definition**: Creates new objects by cloning an existing object (the prototype) rather than instantiating from scratch. **Intuition**: Imagine a game engine spawning hundreds of enemies. Building each enemy from raw configuration files is slow. Instead, you build one "template" enemy, then clone it. Cloning is cheap; full construction is expensive. **Structure**: ```mermaid classDiagram class Prototype { <<interface>> +clone() Prototype } class ConcretePrototype { -field1 -field2 +clone() ConcretePrototype } class Client { +operation() } Prototype <|.. ConcretePrototype Client --> Prototype ``` **Implementation in Python**: ```python import copy from dataclasses import dataclass, field @dataclass class Enemy: name: str health: int weapons: list = field(default_factory=list) abilities: dict = field(default_factory=dict) def clone(self) -> "Enemy": return copy.deepcopy(self) # Pre-configure a prototype goblin_template = Enemy( name="Goblin", health=50, weapons=["dagger"], abilities={"stealth": 0.7} ) # Spawn many cheaply swarm = [goblin_template.clone() for _ in range(100)] swarm[0].name = "Goblin Captain" swarm[0].health = 80 ``` **Use cases**: Game development, document editors (duplicating elements), configuration templating, machine learning experiment templates, infrastructure-as-code resource templating. **Trade-offs**: - Cloning complex objects with circular references or non-serializable fields is tricky. - Shallow vs deep copy must be chosen deliberately. - Far faster than reconstructing from scratch for heavyweight objects. ### Dependency Injection **Definition**: A design technique in which an object receives its dependencies from an external source rather than constructing them itself. While sometimes treated as a principle rather than a "pattern," Dependency Injection (DI) is the dominant creational mechanism in modern enterprise software. **Intuition**: Instead of a class going out and finding its collaborators ("I need a database — let me create one"), the collaborators are handed to it ("Here is the database you need"). This inversion of control makes systems testable, configurable, and modular. There are three primary forms of injection: - **Constructor injection** (preferred): dependencies passed via the constructor. - **Setter injection**: dependencies set via public setter methods after construction. - **Interface injection**: the class implements an interface declaring its dependencies. **Structure**: ```mermaid flowchart LR Container[DI Container] -->|injects| Service[UserService] Container -->|injects| Repo[UserRepository] Service -->|depends on| Repo Config[Configuration / Annotations] --> Container ``` **Implementation in Java with Spring**: ```java public interface UserRepository { User findById(long id); } @Repository public class JpaUserRepository implements UserRepository { public User findById(long id) { /* ... */ return null; } } @Service public class UserService { private final UserRepository repository; @Autowired public UserService(UserRepository repository) { this.repository = repository; } public User getUser(long id) { return repository.findById(id); } } ``` **Implementation in Python (manual injection)**: ```python from typing import Protocol class UserRepository(Protocol): def find_by_id(self, user_id: int) -> dict: ... class PostgresUserRepository: def find_by_id(self, user_id: int) -> dict: return {"id": user_id, "name": "Alice"} class UserService: def __init__(self, repository: UserRepository): self._repo = repository def get_user(self, user_id: int) -> dict: return self._repo.find_by_id(user_id) # Composition root repo = PostgresUserRepository() service = UserService(repo) ``` **Use cases**: Every modern backend framework — Spring (Java), ASP.NET Core (C#), NestJS (TypeScript), FastAPI (Python with `Depends`), Dagger and Hilt (Android), Angular (frontend). **Trade-offs**: - Massively improves testability — dependencies can be mocked or stubbed. - Decouples object construction from object behavior. - Container-based DI can obscure the dependency graph; debugging requires understanding the container. - Misuse leads to "service locator" anti-pattern or over-injection. !!! note Dependency Injection is the foundation of most modern enterprise systems. Mastering it is essential for backend engineering interviews and real-world architecture work. ## Scalability Considerations Creational patterns themselves are class-level constructs, but they affect system-level scalability in important ways. **Singleton and concurrency** — Singletons in multi-threaded environments must be implemented carefully. Lock contention on a shared instance can become a bottleneck. Stateless singletons scale well; stateful singletons may need synchronization or thread-local storage. **Object pooling vs prototype** — When object construction is expensive (e.g., database connections), pooling pre-built objects is often more scalable than constructing on demand. Prototype patterns can support pooling. **DI container performance** — In high-throughput systems, the cost of resolving dependencies on each request matters. Most modern containers cache singleton-scoped beans and resolve transient ones efficiently, but reflection-based DI is slower than compile-time DI (e.g., Dagger). **Factories and elasticity** — In cloud-native systems, factories often produce clients to remote services. Choosing the right factory at runtime supports multi-region failover and elastic scaling. **Horizontal scaling** — When scaling horizontally, ensure singleton-scoped services do not hold per-node state that should be shared. Otherwise, move shared state to distributed caches or databases. **Fault tolerance** — Factories make it easy to swap a failing implementation for a fallback (e.g., a circuit-breaker-wrapped client). ## Security Considerations Object creation has direct security implications. **Singletons holding secrets** — Configuration singletons often hold API keys, database credentials, and tokens. They must be initialized from secure sources (environment variables, secret managers like AWS Secrets Manager or HashiCorp Vault), never hard-coded. **DI configuration as attack surface** — In some frameworks, DI configuration is driven by classpath scanning or runtime reflection. Malicious classes on the classpath could be injected. Use explicit configuration in security-sensitive applications. **Deserialization vulnerabilities** — Prototype cloning that uses Java serialization can be exploited to instantiate arbitrary classes. Prefer copy constructors or explicit clone methods. **Factory poisoning** — A factory whose selection logic relies on user input must validate that input rigorously. Otherwise, an attacker may force creation of a privileged implementation. **Least privilege in constructed objects** — Objects should be constructed with the minimum permissions they need. DI containers should not inject privileged objects into low-trust components. ## Database Design Considerations Creational patterns interact heavily with data layers. **Connection management** — Database connections are typically managed as singletons (or singleton-scoped beans) wrapping connection pools (HikariCP, PgBouncer). Constructing connections per request is catastrophic for performance. **ORM entity factories** — Object-relational mappers like Hibernate and Entity Framework use factory patterns internally to materialize entities from result sets. Lazy loading uses proxy objects produced by these factories. **Repository pattern with DI** — Repositories are typically singleton-scoped services injected into other services. They abstract data access behind interfaces. **Polyglot persistence** — When a system uses multiple databases (SQL for transactions, NoSQL for analytics), factories or abstract factories help select the right client. **ACID vs BASE choices** — Factories can dynamically pick a strongly consistent (ACID) or eventually consistent (BASE) data store depending on the operation, enabling fine-grained CAP trade-offs. ## Cloud and DevOps Perspective Modern cloud-native systems use creational patterns at every layer. **Infrastructure SDKs** — AWS SDK v2 uses Builder patterns extensively (`S3Client.builder().region(...).build()`). Azure and Google Cloud SDKs follow similar conventions. **Kubernetes client factories** — The Kubernetes Java/Go clients use factories to produce typed API clients (`CoreV1Api`, `AppsV1Api`). **Dependency injection in Kubernetes operators** — Frameworks like Quarkus and Spring Boot ship DI-aware native images, optimized for serverless and Kubernetes deployments. **Twelve-Factor App** — The "config" factor (factor III) recommends loading configuration from environment variables, typically via a singleton configuration manager. **Service mesh integration** — In meshes like Istio, sidecar-injected proxies are created by Kubernetes admission controllers — themselves a form of factory. **CI/CD pipelines** — Build pipelines often produce different artifacts (Docker images, native binaries) using builder-like declarative steps in YAML. ## Real-World Examples **Spring Framework (Java)** — Possibly the largest DI container in production worldwide. Every `@Component`, `@Service`, `@Repository` is constructed and wired by Spring's `ApplicationContext`. **Netflix** — Uses Guice for dependency injection across its microservices. Factories are used to construct Hystrix circuit-breaker-wrapped clients. **Android (Google)** — Hilt (built on Dagger) provides compile-time DI for mobile applications, avoiding reflection costs. **Unreal Engine and Unity** — Heavy use of Prototype (cloning prefabs) for spawning game entities. **Amazon Web Services** — All AWS SDK clients are created via Builder patterns. AWS service clients are typically used as application-scoped singletons. **Kubernetes** — The control plane uses factory patterns extensively for constructing resource clients and admission webhooks. **Logging libraries (Log4j, SLF4J, Logback)** — Logger acquisition uses Factory Methods, often returning singletons keyed by logger name. **OkHttp (Square)** — A canonical example of the Builder pattern for HTTP requests and clients. ## Advantages and Disadvantages **Advantages**: - Decouples client code from concrete classes - Improves testability through substitutable dependencies - Encapsulates complex construction logic - Enables runtime polymorphism in object creation - Supports the Open/Closed Principle for adding new object types - Centralizes lifecycle management **Disadvantages**: - Adds indirection that can obscure simple flows - Increases the number of classes and configuration files - DI containers can be hard to debug when misconfigured - Overuse leads to "pattern soup" — code where patterns hide rather than reveal intent - Singletons can introduce hidden global state - Reflection-based DI has runtime cost **When to use**: - Object construction is complex, conditional, or has many variations - Multiple implementations of an abstraction must be selected at runtime - The system needs to be testable through mocking - Object lifetimes must be managed centrally **When not to use**: - Simple value objects or DTOs that need no abstraction - Throwaway scripts and one-off tools - Performance-critical hot paths where indirection costs matter (with measurement) ## Common Interview Questions **Conceptual**: - Explain the difference between Factory Method and Abstract Factory. - Why is Singleton considered an anti-pattern by some engineers? - What is the difference between constructor injection and setter injection? Which is preferred and why? - How does the Builder pattern enforce immutability? - When would you use Prototype over Factory Method? **Scenario-based**: - Design a configuration manager that supports loading from files, environment variables, and remote services. Which patterns apply? - You need to support payment processing across Stripe, PayPal, and Razorpay. Design the architecture. - How would you implement a thread-safe Singleton in Java without using `synchronized` on every access? - A legacy class uses `new` everywhere and cannot be unit tested. How do you refactor it? - Your DI container has a circular dependency error. What strategies can resolve it? **Architecture**: - How does Spring's ApplicationContext implement DI internally? - Compare Dagger (compile-time DI) with Spring (runtime DI). Trade-offs? - Design an extensible plugin system using creational patterns. - How do creational patterns interact with the Strategy and Observer patterns? ## Common Mistakes and Misconceptions **Singleton is a global variable in disguise** — Many engineers reach for Singleton when they should use a DI-managed singleton-scoped bean. The former pollutes global state; the latter is testable and configurable. **Confusing Factory Method with Abstract Factory** — Factory Method produces a single product type via inheritance. Abstract Factory produces a family of related products, typically via composition. **Using Builder for trivial objects** — A Builder for a class with two fields adds noise without value. Use Builders when complexity justifies them. **Deep vs shallow cloning errors in Prototype** — Cloning often produces objects that share mutable internal state with the prototype, leading to subtle bugs. **Service locator masquerading as DI** — Pulling dependencies from a static context inside a method is service location, not dependency injection. It hides dependencies and harms testability. **Over-engineering** — Applying patterns reflexively rather than in response to real complexity is one of the most common mistakes among intermediate engineers. **Treating DI as magic** — DI containers are concrete, debuggable systems. Engineers should understand how their container resolves graphs, not treat it as a black box. ## Diagram Representation **Overall creational pattern landscape**: ```mermaid flowchart TD Client -->|requests object| CreationMechanism CreationMechanism --> Singleton CreationMechanism --> FactoryMethod CreationMechanism --> AbstractFactory CreationMechanism --> Builder CreationMechanism --> Prototype CreationMechanism --> DIContainer Singleton --> Product FactoryMethod --> Product AbstractFactory --> ProductFamily Builder --> ComplexProduct Prototype --> ClonedProduct DIContainer --> Product ``` **DI request lifecycle in a backend service**: ```mermaid sequenceDiagram participant Client participant Controller participant Container as DI Container participant Service as UserService participant Repo as UserRepository participant DB as Database Client->>Controller: HTTP GET /users/42 Controller->>Container: resolve(UserService) Container->>Container: lookup UserService bean Container->>Container: resolve(UserRepository) Container-->>Controller: UserService instance Controller->>Service: getUser(42) Service->>Repo: findById(42) Repo->>DB: SELECT * FROM users WHERE id=42 DB-->>Repo: row Repo-->>Service: User entity Service-->>Controller: User DTO Controller-->>Client: 200 OK ``` **Abstract Factory family selection**: ```mermaid flowchart LR Config[Runtime Config] --> Selector{Platform?} Selector -->|Windows| WF[WindowsFactory] Selector -->|macOS| MF[MacFactory] Selector -->|Linux| LF[LinuxFactory] WF --> WUI[Windows UI Family] MF --> MUI[Mac UI Family] LF --> LUI[Linux UI Family] ``` **Builder construction flow**: ```mermaid sequenceDiagram participant Client participant Builder participant Product Client->>Builder: newBuilder() Client->>Builder: url("...") Client->>Builder: method("POST") Client->>Builder: header("k","v") Client->>Builder: body("...") Client->>Builder: build() Builder->>Product: new(this) Product-->>Client: immutable HttpRequest ``` ## Admonitions and Notes !!! warning Avoid using Singleton as a substitute for proper dependency management. Hidden global state is one of the leading causes of untestable code in legacy systems. !!! tip When a constructor exceeds four parameters or has many optional fields, reach for the Builder pattern. Modern languages and libraries (Kotlin data classes, Java records, Lombok `@Builder`) make this near-free. !!! note Dependency Injection containers are not "free" — they trade construction simplicity for configuration complexity. Always understand how your container resolves the object graph. !!! danger Never expose factory selection logic directly to untrusted user input without validation. This can lead to instantiation-based security vulnerabilities. !!! info In high-performance systems, compile-time DI (Dagger, Micronaut) often outperforms runtime DI (Spring) because it avoids reflection. ## Code and Configuration Examples **Spring Boot configuration (YAML)**: ```yaml spring: datasource: url: jdbc:postgresql://db:5432/app username: ${DB_USER} password: ${DB_PASSWORD} hikari: maximum-pool-size: 20 jpa: hibernate: ddl-auto: validate app: notification: provider: email ``` **Docker Compose with DI-driven service**: ```yaml version: "3.9" services: api: build: . environment: - DB_USER=appuser - DB_PASSWORD=secret - NOTIFICATION_PROVIDER=sms depends_on: - db db: image: postgres:16 environment: - POSTGRES_USER=appuser - POSTGRES_PASSWORD=secret ``` **Kubernetes deployment using config-driven factory selection**: ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: user-service spec: replicas: 3 selector: matchLabels: { app: user-service } template: metadata: labels: { app: user-service } spec: containers: - name: api image: registry/user-service:1.4.0 env: - name: NOTIFICATION_PROVIDER value: "email" - name: PAYMENT_GATEWAY value: "stripe" ``` **Nginx as a reverse proxy in front of a DI-driven app**: ```nginx upstream user_service { server user-service-1:8080; server user-service-2:8080; server user-service-3:8080; } server { listen 80; location /api/ { proxy_pass http://user_service/; proxy_set_header Host $host; } } ``` ## Performance and Trade-Off Analysis | Concern | Direct `new` | Factory | Builder | DI Container | |||||| | Construction speed | Fastest | Fast | Fast | Slower (reflection) | | Flexibility | Lowest | High | Medium | Highest | | Testability | Lowest | High | High | Highest | | Code complexity | Lowest | Medium | Medium | High initially | | Runtime configurability | None | Medium | Medium | High | **Key trade-offs**: - **Simplicity vs flexibility** — Direct instantiation is simple but rigid; DI is flexible but adds infrastructure. - **Compile-time vs runtime** — Compile-time DI catches errors early but limits dynamism. Runtime DI is more flexible but defers errors. - **Eager vs lazy creation** — Singletons can be created eagerly (predictable startup) or lazily (faster startup, possible first-request latency). - **Static vs dynamic factories** — Static factory methods are simple but harder to mock. Instance-based factories are testable but require injection. - **Cloning vs construction** — Prototype is fast for heavy objects but error-prone with deep references. Architects choose based on system requirements: a microservice optimized for cold-start latency may avoid heavy DI containers; an enterprise monolith may embrace them. ## Example System Design Walkthrough **System**: A multi-channel Notification Service. **Requirements**: - Support email, SMS, push notifications, and Slack messages. - New channels must be addable without modifying existing code. - Each channel has its own configuration (SMTP, Twilio, FCM, webhook URLs). - The system must be testable in isolation. - Configuration is environment-driven. **High-Level Design**: ```mermaid flowchart LR Client[Client Service] --> API[Notification API] API --> Dispatcher[NotificationDispatcher] Dispatcher --> Factory[NotificationFactory] Factory --> Email[EmailNotifier] Factory --> SMS[SMSNotifier] Factory --> Push[PushNotifier] Factory --> Slack[SlackNotifier] Container[DI Container] -.->|injects| Dispatcher Container -.->|injects| Factory Config[(Config Source)] --> Container ``` **Pattern application**: - **Abstract Factory**: `NotificationFactory` produces a family of `Notifier` implementations. - **Factory Method**: Each notifier subtype is constructed via a method based on a `ChannelType` enum. - **Builder**: Each notification message (subject, body, recipients, priority, attachments) is constructed using a `NotificationMessage.Builder`. - **Singleton (DI-scoped)**: The dispatcher and factory are singleton-scoped beans managed by the DI container. - **Dependency Injection**: All notifiers are injected into the dispatcher; no hard-coded dependencies. - **Prototype**: Common message templates (e.g., welcome email) are stored as prototypes and cloned for each user. **Database Design**: - `notifications` table (id, user_id, channel, status, payload, sent_at) — relational, ACID. - `templates` table (id, channel, subject, body_template) — relational. - A Redis cache stores rate-limit counters per user per channel. **Scaling Strategy**: - Horizontal scaling of the API and dispatcher behind a load balancer. - Queue-based asynchronous dispatch (RabbitMQ or Kafka) so notification spikes do not overload providers. - Channel-specific worker pools, with retry/backoff per provider. **Bottlenecks and Optimizations**: - External provider latency → use circuit breakers and bulkheads. - Template rendering cost → cache pre-rendered templates as prototypes. - DI container startup time → use compile-time DI (Micronaut/Quarkus) for cold-start-sensitive deployments. **Pattern justification**: - New channels (e.g., WhatsApp) require only a new `Notifier` implementation and a factory entry — OCP is preserved. - Each notifier can be unit-tested with mocked external clients. - Configuration changes require no code modifications. ## Practice Questions **Beginner**: 1. Explain why hard-coding `new SomeClass()` inside business logic makes the code harder to test. 2. Implement a thread-safe Singleton in your preferred language without using double-checked locking. 3. List three real-world scenarios where the Builder pattern is more appropriate than a constructor. **Intermediate**: 4. Refactor a class with a 10-parameter constructor into a Builder-based design. Identify which fields should be mandatory and which optional. 5. Design a small plugin system where new file format parsers (JSON, XML, YAML) can be added without modifying the core parser interface. Specify which creational patterns you used and why. 6. Compare Factory Method and Abstract Factory using a UI toolkit example. Show class diagrams. 7. Demonstrate how Spring (or any DI container) resolves a transitive dependency graph. Describe what happens when a circular dependency is detected. **Advanced**: 8. You are designing a multi-tenant SaaS where each tenant requires a different storage backend (Postgres, MongoDB, DynamoDB). Design a creational architecture that selects the correct backend per request without leaking configuration into business code. 9. Discuss the trade-offs of compile-time DI (Dagger, Micronaut) versus runtime DI (Spring). Under what conditions would you choose each? 10. A high-frequency trading system rejects all forms of dynamic dispatch and reflection for latency reasons. How would you achieve the benefits of DI without using a runtime container? ## Real-World Applications and Use Cases - **Enterprise web services** — DI-driven Spring Boot and ASP.NET Core applications form the backbone of corporate IT. - **Cloud SDKs** — AWS, Azure, and GCP client libraries are textbook examples of Builder and Factory usage. - **Mobile applications** — Hilt/Dagger on Android, and Swift's property wrappers, structure dependency graphs at compile time. - **Game engines** — Unity prefabs and Unreal Engine blueprint instances use Prototype for entity spawning. - **CI/CD systems** — Jenkins, GitHub Actions, and GitLab CI use builder-style declarative configuration. - **API frameworks** — FastAPI's `Depends`, NestJS's modules, and Angular's injectors implement DI for HTTP applications. - **Microservice meshes** — Service discovery and client construction in Istio, Linkerd, and Consul rely on factory abstractions. - **Testing frameworks** — Mockito, Moq, and unittest.mock leverage DI to substitute test doubles for real dependencies. ## Summary Creational design patterns control *how* objects come into existence, separating construction from use. They are foundational to building large-scale systems that remain flexible, testable, and maintainable over time. The six patterns covered each address a distinct construction concern: - **Singleton** ensures a single, shared instance. - **Factory Method** delegates instantiation choice to subclasses. - **Abstract Factory** produces families of related objects. - **Builder** constructs complex objects step-by-step with readable, immutable APIs. - **Prototype** clones pre-configured objects instead of building from scratch. - **Dependency Injection** inverts control, letting external infrastructure supply dependencies. The most important architectural lessons: - Direct instantiation tightly couples components and limits flexibility. - Patterns are tools, not goals — use them when complexity justifies them. - Dependency Injection underpins almost every modern enterprise framework. - Singleton is the most overused and most misused pattern; prefer DI-managed singletons. - Creational decisions ripple outward into scalability, testability, and security. A skilled architect uses creational patterns deliberately: enough to manage real complexity, but no more. The goal is not to apply patterns for their own sake, but to produce systems that survive years of evolving requirements.

    Import from clipboard

    Paste your markdown or webpage here...

    Advanced permission required

    Your current role can only read. Ask the system administrator to acquire write and comment permission.

    This team is disabled

    Sorry, this team is disabled. You can't edit this note.

    This note is locked

    Sorry, only owner can edit this note.

    Reach the limit

    Sorry, you've reached the max length this note can be.
    Please reduce the content or divide it to more notes, thank you!

    Import from Gist

    Import from Snippet

    or

    Export to Snippet

    Are you sure?

    Do you really want to delete this note?
    All users will lose their connection.

    Create a note from template

    Create a note from template

    Oops...
    This template has been removed or transferred.
    Upgrade
    All
    • All
    • Team
    No template.

    Create a template

    Upgrade

    Delete template

    Do you really want to delete this template?
    Turn this template into a regular note and keep its content, versions, and comments.

    This page need refresh

    You have an incompatible client version.
    Refresh to update.
    New version available!
    See releases notes here
    Refresh to enjoy new features.
    Your user state has changed.
    Refresh to load new user state.

    Sign in

    Forgot password
    or
    Sign in via Google Sign in via Facebook Sign in via X(Twitter) Sign in via GitHub Sign in via Dropbox Sign in with Wallet
    Wallet ( )
    Connect another wallet

    New to HackMD? Sign up

    By signing in, you agree to our terms of service.

    Help

    • English
    • 中文
    • Français
    • Deutsch
    • 日本語
    • Español
    • Català
    • Ελληνικά
    • Português
    • italiano
    • Türkçe
    • Русский
    • Nederlands
    • hrvatski jezik
    • język polski
    • Українська
    • हिन्दी
    • svenska
    • Esperanto
    • dansk

    Documents

    Help & Tutorial

    How to use Book mode

    Slide Example

    API Docs

    Edit in VSCode

    Install browser extension

    Contacts

    Feedback

    Discord

    Send us email

    Resources

    Releases

    Pricing

    Blog

    Policy

    Terms

    Privacy

    Cheatsheet

    Syntax Example Reference
    # Header Header 基本排版
    - Unordered List
    • Unordered List
    1. Ordered List
    1. Ordered List
    - [ ] Todo List
    • Todo List
    > Blockquote
    Blockquote
    **Bold font** Bold font
    *Italics font* Italics font
    ~~Strikethrough~~ Strikethrough
    19^th^ 19th
    H~2~O H2O
    ++Inserted text++ Inserted text
    ==Marked text== Marked text
    [link text](https:// "title") Link
    ![image alt](https:// "title") Image
    `Code` Code 在筆記中貼入程式碼
    ```javascript
    var i = 0;
    ```
    var i = 0;
    :smile: :smile: Emoji list
    {%youtube youtube_id %} Externals
    $L^aT_eX$ LaTeX
    :::info
    This is a alert area.
    :::

    This is a alert area.

    Versions and GitHub Sync
    Get Full History Access

    • Edit version name
    • Delete

    revision author avatar     named on  

    More Less

    Note content is identical to the latest version.
    Compare
      Choose a version
      No search result
      Version not found
    Sign in to link this note to GitHub
    Learn more
    This note is not linked with GitHub
     

    Feedback

    Submission failed, please try again

    Thanks for your support.

    On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

    Please give us some advice and help us improve HackMD.

     

    Thanks for your feedback

    Remove version name

    Do you want to remove this version name and description?

    Transfer ownership

    Transfer to
      Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

        Link with GitHub

        Please authorize HackMD on GitHub
        • Please sign in to GitHub and install the HackMD app on your GitHub repo.
        • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
        Learn more  Sign in to GitHub

        Push the note to GitHub Push to GitHub Pull a file from GitHub

          Authorize again
         

        Choose which file to push to

        Select repo
        Refresh Authorize more repos
        Select branch
        Select file
        Select branch
        Choose version(s) to push
        • Save a new version and push
        • Choose from existing versions
        Include title and tags
        Available push count

        Pull from GitHub

         
        File from GitHub
        File from HackMD

        GitHub Link Settings

        File linked

        Linked by
        File path
        Last synced branch
        Available push count

        Danger Zone

        Unlink
        You will no longer receive notification when GitHub file changes after unlink.

        Syncing

        Push failed

        Push successfully