Subscribe To Our Newsletter
You will receive our latest post and tutorial.
Thank you for subscribing!

required
required


Prototype Pattern

Introduction

The Prototype pattern creates new objects by cloning an existing instance rather than building one from scratch. When object creation is expensive — involving database queries, file I/O, or complex initialization — copying a pre-configured prototype is significantly faster. This pattern gives you a way to produce new instances with a known-good starting state.

The Problem

Imagine a document management system where users create reports from templates. Each template has a predefined layout, styling, headers, footers, and metadata. Constructing a new document from scratch every time means repeating the same expensive setup — loading fonts, parsing layout rules, and applying styling. Worse, if users frequently create variations of the same template, you are doing redundant work over and over.

The Solution

The Prototype pattern maintains a registry of pre-built template objects. When a user needs a new document, the system clones the appropriate prototype and lets the user customize only what is different. The clone operation copies the full internal state, so the new document starts fully configured and ready for editing — without repeating the original setup cost.

Key Principle

Create by copying, not constructing. The prototype encapsulates the knowledge of how to replicate itself, so clients never need to know the details of initialization. This is especially powerful when the class hierarchy is complex or the exact type is determined at runtime.

Java Example

This example implements a document template system where reports are cloned from pre-built prototypes.

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

// Prototype interface
public interface DocumentPrototype {
    DocumentPrototype cloneDocument();
}

// Concrete prototype
public class ReportDocument implements DocumentPrototype {
    private String title;
    private String templateType;
    private List<String> sections;
    private Map<String, String> metadata;
    private String headerText;
    private String footerText;
    private String fontFamily;
    private int fontSize;

    public ReportDocument() {
        this.sections = new ArrayList<>();
        this.metadata = new HashMap<>();
    }

    // Deep copy constructor
    private ReportDocument(ReportDocument source) {
        this.title = source.title;
        this.templateType = source.templateType;
        this.sections = new ArrayList<>(source.sections);
        this.metadata = new HashMap<>(source.metadata);
        this.headerText = source.headerText;
        this.footerText = source.footerText;
        this.fontFamily = source.fontFamily;
        this.fontSize = source.fontSize;
    }

    @Override
    public DocumentPrototype cloneDocument() {
        return new ReportDocument(this);
    }

    // Setters for configuration
    public void setTitle(String title) { this.title = title; }
    public void setTemplateType(String type) { this.templateType = type; }
    public void addSection(String section) { this.sections.add(section); }
    public void setMetadata(String key, String value) { this.metadata.put(key, value); }
    public void setHeaderText(String text) { this.headerText = text; }
    public void setFooterText(String text) { this.footerText = text; }
    public void setFont(String family, int size) {
        this.fontFamily = family;
        this.fontSize = size;
    }

    @Override
    public String toString() {
        return "ReportDocument{" +
            "title='" + title + "'" +
            ", type='" + templateType + "'" +
            ", sections=" + sections.size() +
            ", font=" + fontFamily + " " + fontSize + "pt" +
            "}";
    }
}

// Template registry
public class DocumentTemplateRegistry {
    private final Map<String, DocumentPrototype> templates = new HashMap<>();

    public void registerTemplate(String name, DocumentPrototype prototype) {
        templates.put(name, prototype);
    }

    public DocumentPrototype createFromTemplate(String name) {
        DocumentPrototype prototype = templates.get(name);
        if (prototype == null) {
            throw new IllegalArgumentException("Unknown template: " + name);
        }
        return prototype.cloneDocument();
    }
}

// Usage
public class Application {
    public static void main(String[] args) {
        // Build expensive prototypes once
        ReportDocument financialTemplate = new ReportDocument();
        financialTemplate.setTitle("Quarterly Financial Report");
        financialTemplate.setTemplateType("financial");
        financialTemplate.setFont("Arial", 11);
        financialTemplate.setHeaderText("CONFIDENTIAL - Financial Report");
        financialTemplate.setFooterText("Generated by Report System v2.0");
        financialTemplate.addSection("Executive Summary");
        financialTemplate.addSection("Revenue Analysis");
        financialTemplate.addSection("Expense Breakdown");
        financialTemplate.addSection("Projections");
        financialTemplate.setMetadata("department", "Finance");
        financialTemplate.setMetadata("classification", "confidential");

        ReportDocument techTemplate = new ReportDocument();
        techTemplate.setTitle("Technical Review");
        techTemplate.setTemplateType("technical");
        techTemplate.setFont("Courier New", 10);
        techTemplate.setHeaderText("Engineering - Technical Review");
        techTemplate.setFooterText("Internal Use Only");
        techTemplate.addSection("Architecture Overview");
        techTemplate.addSection("Performance Metrics");
        techTemplate.addSection("Risk Assessment");
        techTemplate.setMetadata("department", "Engineering");

        // Register prototypes
        DocumentTemplateRegistry registry = new DocumentTemplateRegistry();
        registry.registerTemplate("financial", financialTemplate);
        registry.registerTemplate("technical", techTemplate);

        // Clone and customize
        ReportDocument q1Report = (ReportDocument) registry.createFromTemplate("financial");
        q1Report.setTitle("Q1 2025 Financial Report");
        System.out.println(q1Report);

        ReportDocument q2Report = (ReportDocument) registry.createFromTemplate("financial");
        q2Report.setTitle("Q2 2025 Financial Report");
        System.out.println(q2Report);

        ReportDocument apiReview = (ReportDocument) registry.createFromTemplate("technical");
        apiReview.setTitle("API Gateway Technical Review");
        System.out.println(apiReview);
    }
}

Python Example

The same document template system in Python, using the copy module for deep cloning.

import copy
from typing import Optional


class ReportDocument:
    def __init__(self):
        self.title: str = ""
        self.template_type: str = ""
        self.sections: list[str] = []
        self.metadata: dict[str, str] = {}
        self.header_text: str = ""
        self.footer_text: str = ""
        self.font_family: str = "Arial"
        self.font_size: int = 12

    def clone(self) -> "ReportDocument":
        """Deep copy this document to create an independent clone."""
        return copy.deepcopy(self)

    def __str__(self):
        return (
            f"ReportDocument(title='{self.title}'"
            f", type='{self.template_type}'"
            f", sections={len(self.sections)}"
            f", font={self.font_family} {self.font_size}pt)"
        )


class DocumentTemplateRegistry:
    def __init__(self):
        self._templates: dict[str, ReportDocument] = {}

    def register(self, name: str, prototype: ReportDocument):
        self._templates[name] = prototype

    def create_from_template(self, name: str) -> ReportDocument:
        prototype = self._templates.get(name)
        if prototype is None:
            raise ValueError(f"Unknown template: {name}")
        return prototype.clone()


# Usage
if __name__ == "__main__":
    # Build expensive prototypes once
    financial_template = ReportDocument()
    financial_template.title = "Quarterly Financial Report"
    financial_template.template_type = "financial"
    financial_template.font_family = "Arial"
    financial_template.font_size = 11
    financial_template.header_text = "CONFIDENTIAL - Financial Report"
    financial_template.footer_text = "Generated by Report System v2.0"
    financial_template.sections = [
        "Executive Summary",
        "Revenue Analysis",
        "Expense Breakdown",
        "Projections",
    ]
    financial_template.metadata = {
        "department": "Finance",
        "classification": "confidential",
    }

    tech_template = ReportDocument()
    tech_template.title = "Technical Review"
    tech_template.template_type = "technical"
    tech_template.font_family = "Courier New"
    tech_template.font_size = 10
    tech_template.header_text = "Engineering - Technical Review"
    tech_template.footer_text = "Internal Use Only"
    tech_template.sections = [
        "Architecture Overview",
        "Performance Metrics",
        "Risk Assessment",
    ]
    tech_template.metadata = {"department": "Engineering"}

    # Register prototypes
    registry = DocumentTemplateRegistry()
    registry.register("financial", financial_template)
    registry.register("technical", tech_template)

    # Clone and customize
    q1_report = registry.create_from_template("financial")
    q1_report.title = "Q1 2025 Financial Report"
    print(q1_report)

    q2_report = registry.create_from_template("financial")
    q2_report.title = "Q2 2025 Financial Report"
    print(q2_report)

    api_review = registry.create_from_template("technical")
    api_review.title = "API Gateway Technical Review"
    print(api_review)

    # Prove independence — original is unchanged
    print(f"Original title: {financial_template.title}")
    print(f"Clone title: {q1_report.title}")

When to Use

  • Expensive initialization — when constructing an object requires costly operations like database queries, network calls, or file parsing, and you need many similar instances.
  • Template systems — when users create variations of pre-built templates (documents, emails, configurations) and only modify a few fields from the default.
  • Runtime type determination — when you do not know the exact class at compile time and need to create copies of objects received through an interface.
  • Undo/snapshot functionality — when you need to save and restore object states, cloning provides a natural way to capture snapshots.

Real-World Usage

  • Java Object.clone() — the Cloneable interface and clone() method are built into Java’s object model, though deep copy must be implemented manually.
  • Python copy modulecopy.copy() and copy.deepcopy() provide shallow and deep cloning for any Python object.
  • JavaScript Object.assign() / structuredClone() — used to clone configuration objects and state in frontend frameworks.
  • Unity EngineInstantiate() clones game objects with all their components, used heavily for spawning enemies, projectiles, and particles.
  • Spring Framework — prototype-scoped beans create a new clone for each injection point, unlike the default singleton scope.
August 4, 2019

Facade Pattern

Introduction

The Facade Pattern is a structural design pattern that provides a simplified, unified interface to a complex subsystem. Think of ordering food at a restaurant — you simply tell the waiter what you want. Behind the scenes, the kitchen, inventory, and billing systems coordinate to fulfill your order. The waiter is the facade.

In software, complex operations often involve orchestrating multiple services, classes, or APIs. A facade wraps this complexity behind a single, clean method call that clients can use without understanding the internals.

The Problem

Processing an e-commerce order involves multiple subsystems: validating inventory, charging the payment method, generating an invoice, sending a confirmation email, and updating the shipping system. Each subsystem has its own interface, configuration, and error handling requirements.

Forcing client code to interact with all these subsystems directly creates tight coupling and duplicates complex orchestration logic everywhere an order is placed — from the web controller, the mobile API, the admin panel, and the batch processing system.

The Solution

Create an OrderFacade class that exposes a single placeOrder() method. Internally, it coordinates all the subsystems in the correct order, handles errors, and returns a clean result. Client code only interacts with the facade, completely shielded from the complexity underneath.

If any subsystem changes its interface or a new step is added to the process, only the facade needs to be updated — not every caller.

Key Principle

The Facade Pattern applies the Principle of Least Knowledge (Law of Demeter) — a client should only talk to its immediate friends, not the internal components of those friends. It reduces coupling between the client and the subsystem.

Java Example

Here we simplify order processing by wrapping multiple subsystems behind a single facade.

// Subsystem: Inventory
public class InventoryService {

    public boolean checkStock(String productId, int quantity) {
        System.out.println("[Inventory] Checking stock for " + productId);
        return true;  // Simulate available stock
    }

    public void reserveStock(String productId, int quantity) {
        System.out.println("[Inventory] Reserved " + quantity + " units of " + productId);
    }
}

// Subsystem: Payment
public class PaymentService {

    public String charge(String customerId, double amount) {
        System.out.println("[Payment] Charging $" + amount + " to customer " + customerId);
        return "TXN-" + System.currentTimeMillis();
    }

    public void refund(String transactionId) {
        System.out.println("[Payment] Refunding transaction " + transactionId);
    }
}

// Subsystem: Invoice
public class InvoiceService {

    public String generateInvoice(String orderId, String customerId, double amount) {
        String invoiceId = "INV-" + System.currentTimeMillis();
        System.out.println("[Invoice] Generated " + invoiceId + " for order " + orderId);
        return invoiceId;
    }
}

// Subsystem: Email
public class EmailService {

    public void sendOrderConfirmation(String customerId, String orderId) {
        System.out.println("[Email] Confirmation sent to customer " + customerId + " for order " + orderId);
    }
}

// Subsystem: Shipping
public class ShippingService {

    public String createShipment(String orderId, String address) {
        String trackingId = "SHIP-" + System.currentTimeMillis();
        System.out.println("[Shipping] Created shipment " + trackingId + " to " + address);
        return trackingId;
    }
}

// Result object
public class OrderResult {
    private String orderId;
    private String transactionId;
    private String trackingId;
    private boolean success;
    private String message;

    // Constructor, getters omitted for brevity
    public OrderResult(String orderId, String transactionId, String trackingId,
                       boolean success, String message) {
        this.orderId = orderId;
        this.transactionId = transactionId;
        this.trackingId = trackingId;
        this.success = success;
        this.message = message;
    }

    @Override
    public String toString() {
        return String.format("Order %s | Txn: %s | Tracking: %s | %s",
            orderId, transactionId, trackingId, message);
    }
}

// FACADE: Simplifies the entire order process
public class OrderFacade {
    private final InventoryService inventory;
    private final PaymentService payment;
    private final InvoiceService invoice;
    private final EmailService email;
    private final ShippingService shipping;

    public OrderFacade() {
        this.inventory = new InventoryService();
        this.payment = new PaymentService();
        this.invoice = new InvoiceService();
        this.email = new EmailService();
        this.shipping = new ShippingService();
    }

    public OrderResult placeOrder(String customerId, String productId,
                                   int quantity, double amount, String address) {
        String orderId = "ORD-" + System.currentTimeMillis();

        // Step 1: Check and reserve inventory
        if (!inventory.checkStock(productId, quantity)) {
            return new OrderResult(orderId, null, null, false, "Out of stock");
        }
        inventory.reserveStock(productId, quantity);

        // Step 2: Process payment
        String transactionId;
        try {
            transactionId = payment.charge(customerId, amount);
        } catch (Exception e) {
            return new OrderResult(orderId, null, null, false, "Payment failed");
        }

        // Step 3: Generate invoice
        invoice.generateInvoice(orderId, customerId, amount);

        // Step 4: Create shipment
        String trackingId = shipping.createShipment(orderId, address);

        // Step 5: Send confirmation
        email.sendOrderConfirmation(customerId, orderId);

        return new OrderResult(orderId, transactionId, trackingId, true, "Order placed successfully");
    }
}

// Usage - Client code is clean and simple
public class Main {
    public static void main(String[] args) {
        OrderFacade orderFacade = new OrderFacade();

        OrderResult result = orderFacade.placeOrder(
            "CUST-42", "PROD-100", 2, 59.98, "123 Main St, Springfield"
        );

        System.out.println(result);
    }
}

Python Example

The same order processing facade scenario in Python.

from dataclasses import dataclass
import time


# Subsystem: Inventory
class InventoryService:

    def check_stock(self, product_id: str, quantity: int) -> bool:
        print(f"[Inventory] Checking stock for {product_id}")
        return True

    def reserve_stock(self, product_id: str, quantity: int) -> None:
        print(f"[Inventory] Reserved {quantity} units of {product_id}")


# Subsystem: Payment
class PaymentService:

    def charge(self, customer_id: str, amount: float) -> str:
        print(f"[Payment] Charging ${amount} to customer {customer_id}")
        return f"TXN-{int(time.time() * 1000)}"

    def refund(self, transaction_id: str) -> None:
        print(f"[Payment] Refunding transaction {transaction_id}")


# Subsystem: Invoice
class InvoiceService:

    def generate_invoice(self, order_id: str, customer_id: str, amount: float) -> str:
        invoice_id = f"INV-{int(time.time() * 1000)}"
        print(f"[Invoice] Generated {invoice_id} for order {order_id}")
        return invoice_id


# Subsystem: Email
class EmailService:

    def send_order_confirmation(self, customer_id: str, order_id: str) -> None:
        print(f"[Email] Confirmation sent to customer {customer_id} for order {order_id}")


# Subsystem: Shipping
class ShippingService:

    def create_shipment(self, order_id: str, address: str) -> str:
        tracking_id = f"SHIP-{int(time.time() * 1000)}"
        print(f"[Shipping] Created shipment {tracking_id} to {address}")
        return tracking_id


@dataclass
class OrderResult:
    order_id: str
    transaction_id: str
    tracking_id: str
    success: bool
    message: str

    def __str__(self):
        return (f"Order {self.order_id} | Txn: {self.transaction_id} | "
                f"Tracking: {self.tracking_id} | {self.message}")


# FACADE: Simplifies the entire order process
class OrderFacade:

    def __init__(self):
        self._inventory = InventoryService()
        self._payment = PaymentService()
        self._invoice = InvoiceService()
        self._email = EmailService()
        self._shipping = ShippingService()

    def place_order(self, customer_id: str, product_id: str,
                    quantity: int, amount: float, address: str) -> OrderResult:
        order_id = f"ORD-{int(time.time() * 1000)}"

        # Step 1: Check and reserve inventory
        if not self._inventory.check_stock(product_id, quantity):
            return OrderResult(order_id, None, None, False, "Out of stock")
        self._inventory.reserve_stock(product_id, quantity)

        # Step 2: Process payment
        try:
            transaction_id = self._payment.charge(customer_id, amount)
        except Exception:
            return OrderResult(order_id, None, None, False, "Payment failed")

        # Step 3: Generate invoice
        self._invoice.generate_invoice(order_id, customer_id, amount)

        # Step 4: Create shipment
        tracking_id = self._shipping.create_shipment(order_id, address)

        # Step 5: Send confirmation
        self._email.send_order_confirmation(customer_id, order_id)

        return OrderResult(order_id, transaction_id, tracking_id, True, "Order placed successfully")


# Usage - Client code is clean and simple
if __name__ == "__main__":
    order_facade = OrderFacade()

    result = order_facade.place_order(
        "CUST-42", "PROD-100", 2, 59.98, "123 Main St, Springfield"
    )

    print(result)

When to Use

  • Complex subsystem orchestration — When a single operation requires coordinating multiple services in a specific order
  • API simplification — When you want to provide a simple interface to a complex library or framework
  • Layer isolation — When you want to decouple your application layers (e.g., controller layer from service layer internals)
  • Legacy wrapping — When you need to present a modern, clean API over messy legacy code

Real-World Usage

  • Spring Boot’s JdbcTemplate — Simplifies raw JDBC operations (connection management, statement creation, result set handling) behind clean methods
  • SLF4J — Provides a simple logging facade over complex logging frameworks
  • jQuery$.ajax() was a facade over the verbose XMLHttpRequest API
  • Python’s requests library — A facade over the complex urllib / http.client modules
  • AWS SDK — High-level resource interfaces that simplify low-level API calls
August 4, 2019

Factory Method Pattern

Introduction

The Factory Method pattern defines an interface for creating objects but lets subclasses decide which class to instantiate. Instead of calling a constructor directly, client code delegates creation to a factory method, which returns an object that conforms to a common interface. This decouples the client from the concrete classes it uses.

The Problem

Suppose your e-commerce platform needs to process payments through multiple providers — Stripe, PayPal, and Square. If your checkout code directly instantiates StripeProcessor or PayPalProcessor, adding a new provider means modifying the checkout logic. Every conditional branch increases complexity, and testing becomes harder because you cannot easily swap in a mock processor.

The Solution

The Factory Method pattern introduces a creator class with a method that returns a PaymentProcessor interface. Each subclass (or a parameterized factory) decides which concrete processor to instantiate. The checkout code works only with the interface, so adding a new payment provider means adding a new class — no changes to existing code.

Key Principle

Program to an interface, not an implementation. The factory method encapsulates the “new” keyword behind a method call, so the client never depends on concrete classes. This is the Open/Closed Principle in action — open for extension, closed for modification.

Java Example

This example creates payment processors for different providers through a factory method.

import java.math.BigDecimal;

// Common interface for all payment processors
public interface PaymentProcessor {
    boolean charge(String customerId, BigDecimal amount);
    boolean refund(String transactionId, BigDecimal amount);
    String getProviderName();
}

// Concrete implementation: Stripe
public class StripeProcessor implements PaymentProcessor {
    private final String apiKey;

    public StripeProcessor(String apiKey) {
        this.apiKey = apiKey;
    }

    @Override
    public boolean charge(String customerId, BigDecimal amount) {
        System.out.println("[Stripe] Charging $" + amount + " to customer " + customerId);
        // Real Stripe SDK call would go here
        return true;
    }

    @Override
    public boolean refund(String transactionId, BigDecimal amount) {
        System.out.println("[Stripe] Refunding $" + amount + " for txn " + transactionId);
        return true;
    }

    @Override
    public String getProviderName() { return "Stripe"; }
}

// Concrete implementation: PayPal
public class PayPalProcessor implements PaymentProcessor {
    private final String clientId;
    private final String clientSecret;

    public PayPalProcessor(String clientId, String clientSecret) {
        this.clientId = clientId;
        this.clientSecret = clientSecret;
    }

    @Override
    public boolean charge(String customerId, BigDecimal amount) {
        System.out.println("[PayPal] Charging $" + amount + " to customer " + customerId);
        return true;
    }

    @Override
    public boolean refund(String transactionId, BigDecimal amount) {
        System.out.println("[PayPal] Refunding $" + amount + " for txn " + transactionId);
        return true;
    }

    @Override
    public String getProviderName() { return "PayPal"; }
}

// Concrete implementation: Square
public class SquareProcessor implements PaymentProcessor {
    private final String accessToken;

    public SquareProcessor(String accessToken) {
        this.accessToken = accessToken;
    }

    @Override
    public boolean charge(String customerId, BigDecimal amount) {
        System.out.println("[Square] Charging $" + amount + " to customer " + customerId);
        return true;
    }

    @Override
    public boolean refund(String transactionId, BigDecimal amount) {
        System.out.println("[Square] Refunding $" + amount + " for txn " + transactionId);
        return true;
    }

    @Override
    public String getProviderName() { return "Square"; }
}

// Factory that creates the right processor
public class PaymentProcessorFactory {

    public static PaymentProcessor create(String provider) {
        switch (provider.toLowerCase()) {
            case "stripe":
                return new StripeProcessor(System.getenv("STRIPE_API_KEY"));
            case "paypal":
                return new PayPalProcessor(
                    System.getenv("PAYPAL_CLIENT_ID"),
                    System.getenv("PAYPAL_SECRET")
                );
            case "square":
                return new SquareProcessor(System.getenv("SQUARE_ACCESS_TOKEN"));
            default:
                throw new IllegalArgumentException("Unknown provider: " + provider);
        }
    }
}

// Usage
public class CheckoutService {
    public static void main(String[] args) {
        PaymentProcessor processor = PaymentProcessorFactory.create("stripe");

        System.out.println("Using: " + processor.getProviderName());
        processor.charge("cust_123", new BigDecimal("49.99"));
        processor.refund("txn_456", new BigDecimal("10.00"));
    }
}

Python Example

The same payment processor factory in Python, using abstract base classes and a registry-based factory.

from abc import ABC, abstractmethod
from decimal import Decimal
import os


class PaymentProcessor(ABC):
    @abstractmethod
    def charge(self, customer_id: str, amount: Decimal) -> bool:
        pass

    @abstractmethod
    def refund(self, transaction_id: str, amount: Decimal) -> bool:
        pass

    @abstractmethod
    def provider_name(self) -> str:
        pass


class StripeProcessor(PaymentProcessor):
    def __init__(self, api_key: str):
        self._api_key = api_key

    def charge(self, customer_id: str, amount: Decimal) -> bool:
        print(f"[Stripe] Charging ${amount} to customer {customer_id}")
        return True

    def refund(self, transaction_id: str, amount: Decimal) -> bool:
        print(f"[Stripe] Refunding ${amount} for txn {transaction_id}")
        return True

    def provider_name(self) -> str:
        return "Stripe"


class PayPalProcessor(PaymentProcessor):
    def __init__(self, client_id: str, client_secret: str):
        self._client_id = client_id
        self._client_secret = client_secret

    def charge(self, customer_id: str, amount: Decimal) -> bool:
        print(f"[PayPal] Charging ${amount} to customer {customer_id}")
        return True

    def refund(self, transaction_id: str, amount: Decimal) -> bool:
        print(f"[PayPal] Refunding ${amount} for txn {transaction_id}")
        return True

    def provider_name(self) -> str:
        return "PayPal"


class SquareProcessor(PaymentProcessor):
    def __init__(self, access_token: str):
        self._access_token = access_token

    def charge(self, customer_id: str, amount: Decimal) -> bool:
        print(f"[Square] Charging ${amount} to customer {customer_id}")
        return True

    def refund(self, transaction_id: str, amount: Decimal) -> bool:
        print(f"[Square] Refunding ${amount} for txn {transaction_id}")
        return True

    def provider_name(self) -> str:
        return "Square"


class PaymentProcessorFactory:
    _registry = {
        "stripe": lambda: StripeProcessor(os.getenv("STRIPE_API_KEY", "")),
        "paypal": lambda: PayPalProcessor(
            os.getenv("PAYPAL_CLIENT_ID", ""),
            os.getenv("PAYPAL_SECRET", ""),
        ),
        "square": lambda: SquareProcessor(os.getenv("SQUARE_ACCESS_TOKEN", "")),
    }

    @classmethod
    def create(cls, provider: str) -> PaymentProcessor:
        creator = cls._registry.get(provider.lower())
        if not creator:
            raise ValueError(f"Unknown provider: {provider}")
        return creator()


# Usage
if __name__ == "__main__":
    processor = PaymentProcessorFactory.create("stripe")

    print(f"Using: {processor.provider_name()}")
    processor.charge("cust_123", Decimal("49.99"))
    processor.refund("txn_456", Decimal("10.00"))

When to Use

  • Multiple implementations of an interface — when your code needs to work with different concrete classes (payment providers, notification channels, file parsers) that share a common contract.
  • Decoupling creation from usage — when the client should not know or care which concrete class it receives, only that it satisfies the interface.
  • Plugin architectures — when third parties or configuration files determine which implementation to load at runtime.
  • Testing — when you need to swap real implementations for mocks or stubs without changing the code that consumes them.

Real-World Usage

  • JDBC DriverManagerDriverManager.getConnection() returns a Connection implementation based on the JDBC URL, abstracting away MySQL, PostgreSQL, or Oracle specifics.
  • Spring BeanFactory — creates beans by name or type, returning objects that implement requested interfaces.
  • Python’s json.loads() — the decoder factory can be swapped via the cls parameter to return custom object types.
  • SLF4J LoggerFactoryLoggerFactory.getLogger() returns a logger implementation from whichever logging backend is on the classpath (Logback, Log4j, etc.).
August 4, 2019

Builder Pattern

Introduction

The Builder pattern separates the construction of a complex object from its representation, allowing the same construction process to create different representations. It shines when an object requires many configuration options, optional parameters, or a specific construction sequence — making the code readable and maintainable where telescoping constructors would fail.

The Problem

Consider building an HTTP request object. You need a URL, an HTTP method, headers, query parameters, a request body, a timeout value, and retry settings. A constructor with all these parameters is unreadable and error-prone. You cannot tell which argument is which without checking the signature, and adding new options means changing every call site. Optional parameters make this even messier.

The Solution

The Builder pattern provides a step-by-step API for constructing the object. Each method sets one property and returns the builder itself, enabling method chaining. When all desired options are configured, a final build() call produces the immutable result. The object is always in a valid, complete state after construction.

Key Principle

Separate construction from representation. The builder encapsulates the construction logic so the client code reads like a declaration of what the object should look like, not how it gets assembled internally.

Java Example

This example builds an HttpRequest object with a fluent API, supporting method, URL, headers, body, timeout, and retries.

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public 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 final int maxRetries;

    // Private constructor — only the Builder can create instances
    private HttpRequest(Builder builder) {
        this.url = builder.url;
        this.method = builder.method;
        this.headers = Collections.unmodifiableMap(builder.headers);
        this.body = builder.body;
        this.timeoutMs = builder.timeoutMs;
        this.maxRetries = builder.maxRetries;
    }

    public String getUrl() { return url; }
    public String getMethod() { return method; }
    public Map<String, String> getHeaders() { return headers; }
    public String getBody() { return body; }
    public int getTimeoutMs() { return timeoutMs; }
    public int getMaxRetries() { return maxRetries; }

    @Override
    public String toString() {
        return method + " " + url
            + " | headers=" + headers.size()
            + " | timeout=" + timeoutMs + "ms"
            + " | retries=" + maxRetries;
    }

    // Static inner Builder class
    public static class Builder {
        private final String url;       // Required
        private String method = "GET";  // Default
        private Map<String, String> headers = new HashMap<>();
        private String body = null;
        private int timeoutMs = 30000;  // Default 30s
        private int maxRetries = 0;

        public Builder(String url) {
            if (url == null || url.isEmpty()) {
                throw new IllegalArgumentException("URL is required");
            }
            this.url = url;
        }

        public Builder method(String method) {
            this.method = method;
            return this;
        }

        public Builder header(String key, String value) {
            this.headers.put(key, value);
            return this;
        }

        public Builder body(String body) {
            this.body = body;
            return this;
        }

        public Builder timeout(int timeoutMs) {
            this.timeoutMs = timeoutMs;
            return this;
        }

        public Builder retries(int maxRetries) {
            this.maxRetries = maxRetries;
            return this;
        }

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

// Usage
public class Application {
    public static void main(String[] args) {
        HttpRequest request = new HttpRequest.Builder("https://api.example.com/users")
            .method("POST")
            .header("Content-Type", "application/json")
            .header("Authorization", "Bearer token123")
            .body("{\"name\": \"Alice\", \"email\": \"alice@example.com\"}")
            .timeout(5000)
            .retries(3)
            .build();

        System.out.println(request);

        // Simple GET request with defaults
        HttpRequest getRequest = new HttpRequest.Builder("https://api.example.com/health")
            .build();

        System.out.println(getRequest);
    }
}

Python Example

The same HTTP request builder in Python, using method chaining and an immutable result.

from dataclasses import dataclass, field
from typing import Optional


@dataclass(frozen=True)
class HttpRequest:
    url: str
    method: str = "GET"
    headers: dict = field(default_factory=dict)
    body: Optional[str] = None
    timeout_ms: int = 30000
    max_retries: int = 0

    def __str__(self):
        return (
            f"{self.method} {self.url}"
            f" | headers={len(self.headers)}"
            f" | timeout={self.timeout_ms}ms"
            f" | retries={self.max_retries}"
        )


class HttpRequestBuilder:
    def __init__(self, url: str):
        if not url:
            raise ValueError("URL is required")
        self._url = url
        self._method = "GET"
        self._headers = {}
        self._body = None
        self._timeout_ms = 30000
        self._max_retries = 0

    def method(self, method: str) -> "HttpRequestBuilder":
        self._method = method
        return self

    def header(self, key: str, value: str) -> "HttpRequestBuilder":
        self._headers[key] = value
        return self

    def body(self, body: str) -> "HttpRequestBuilder":
        self._body = body
        return self

    def timeout(self, timeout_ms: int) -> "HttpRequestBuilder":
        self._timeout_ms = timeout_ms
        return self

    def retries(self, max_retries: int) -> "HttpRequestBuilder":
        self._max_retries = max_retries
        return self

    def build(self) -> HttpRequest:
        return HttpRequest(
            url=self._url,
            method=self._method,
            headers=dict(self._headers),
            body=self._body,
            timeout_ms=self._timeout_ms,
            max_retries=self._max_retries,
        )


# Usage
if __name__ == "__main__":
    request = (
        HttpRequestBuilder("https://api.example.com/users")
        .method("POST")
        .header("Content-Type", "application/json")
        .header("Authorization", "Bearer token123")
        .body('{"name": "Alice", "email": "alice@example.com"}')
        .timeout(5000)
        .retries(3)
        .build()
    )

    print(request)

    # Simple GET with defaults
    get_request = HttpRequestBuilder("https://api.example.com/health").build()
    print(get_request)

When to Use

  • Objects with many optional parameters — when a constructor would need 5+ parameters and most are optional, a builder makes the code self-documenting.
  • Immutable objects — when you want the final object to be immutable but need flexibility during construction. The builder accumulates state, then produces a frozen result.
  • Complex configuration — building SDK clients, HTTP requests, database queries, or email messages where the construction involves many steps and validation.
  • Test data generation — creating test fixtures with sensible defaults that can be selectively overridden per test case.

Real-World Usage

  • Java StringBuilder — builds strings efficiently through repeated append() calls before producing the final String.
  • OkHttp Request.Builder — constructs HTTP requests with a fluent API for URL, method, headers, and body.
  • Lombok @Builder — auto-generates builder classes for Java POJOs at compile time.
  • Python requests libraryrequests.Request objects can be prepared step-by-step before sending via a Session.
  • Protocol Buffers — all protobuf message classes use builders for construction in both Java and C++.
August 2, 2019

React Components

Components are like functions that return HTML elements. Components are independent and reusable bits of code. They serve the same purpose as JavaScript functions, but work in isolation and returns HTML via a render function. Components come in two types, Class components and Function components, in this tutorial we will concentrate on Class components.

Component Lifecycle

Component as a Class

When creating a React component, the component’s name must start with an upper case letter. The component has to include the extends React.Component statement, this statement creates an inheritance to React.Component, and gives your component access to React.Component’s functions. The component also requires a render() method, this method returns HTML. A component must be exported.

class Home extends React.Component { 
    render() { 
     return <h2>Hello World!</h2>; 
    } 
}
export default Home;

 

Component as a function

Here is the same example as above, but created using a Function component instead. A Function component also returns HTML, and behaves pretty much the same way as a Class component, but Class components have some additions, and will be preferred in this tutorial.

function Home() { 
    return <h2>Hello World!</h2>; 
}

function components are a simpler way to write components that only contain a render method and don’t have their own state. Instead of defining a class which extends React.Component, we can write a function that takes props as input and returns what should be rendered. Function components are less tedious to write than classes, and many components can be expressed this way.

function Home(props) { 
   return <h2>Hello {props.name}!</h2>; 
}

 

Component Constructor

If there is a constructor() function in your component, this function will be called when the component gets initiated. The constructor function is where you initiate the component’s properties. In React, component properties should be kept in an object called state.

You will learn more about state later in this tutorial.

The constructor function is also where you honor the inheritance of the parent component by including the super() statement, which executes the parent component’s constructor function, and your component has access to all the functions of the parent component (React.Component).

class Home extends React.Component { 
      constructor() { 
       super(); 
       this.state = {name: "Folau"}; 
      } 

      render() { 
       return <h2>Hey this.state.name</h2>; 
      } 
}

 

A good rule of thumb is that if a part of your UI is used several times (ButtonPanelAvatar), or is complex enough on its own (AppFeedStoryComment), it is a good candidate to be extracted to a separate component.

All React components must act like pure functions with respect to their props.

July 31, 2019