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.
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 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.
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.
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);
}
}
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}")
Cloneable interface and clone() method are built into Java’s object model, though deep copy must be implemented manually.copy.copy() and copy.deepcopy() provide shallow and deep cloning for any Python object.Instantiate() clones game objects with all their components, used heavily for spawning enemies, projectiles, and particles.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.
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.
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.
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.
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);
}
}
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)
JdbcTemplate — Simplifies raw JDBC operations (connection management, statement creation, result set handling) behind clean methods$.ajax() was a facade over the verbose XMLHttpRequest APIrequests library — A facade over the complex urllib / http.client modulesThe 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.
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 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.
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.
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"));
}
}
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"))
DriverManager.getConnection() returns a Connection implementation based on the JDBC URL, abstracting away MySQL, PostgreSQL, or Oracle specifics.json.loads() — the decoder factory can be swapped via the cls parameter to return custom object types.LoggerFactory.getLogger() returns a logger implementation from whichever logging backend is on the classpath (Logback, Log4j, etc.).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.
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 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.
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.
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);
}
}
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)
append() calls before producing the final String.requests.Request objects can be prepared step-by-step before sending via a Session.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.

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;
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>;
}
If there is a 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 constructor()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 (Button, Panel, Avatar), or is complex enough on its own (App, FeedStory, Comment), 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.