DESIGN PATTERNS (Python)

Design patterns are reusable solutions to common problems in software design. Below are five patterns in Python with short descriptions and code examples.

Decorator

Wraps an object or function to add behavior without changing its core logic. In Python, decorators are often implemented with functions that take a function and return a new function (or with classes that implement __call__).

def log_call(func):
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__}")
        return func(*args, **kwargs)
    return wrapper

@log_call
def greet(name):
    return f"Hello, {name}"

greet("Alice")  # prints "Calling greet", then "Hello, Alice"

Singleton

Ensures only one instance of a class exists. Every time you “create” the object, you get the same instance—useful for configs, database connections, or shared caches.

class Database:
    _instance = None

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

db1 = Database()
db2 = Database()
assert db1 is db2  # same object

Factory

A function or class that creates objects for you instead of calling constructors directly. It hides the concrete type and makes it easy to add new types or switch implementations.

def create_parser(format_type):
    if format_type == "json":
        return JsonParser()
    elif format_type == "xml":
        return XmlParser()
    raise ValueError(f"Unknown format: {format_type}")

parser = create_parser("json")  # returns the right implementation

Facade

Provides a simple, unified interface to a set of subsystems or complex APIs. Callers use one entry point instead of dealing with many low-level details.

class OrderFacade:
    def __init__(self):
        self.inventory = InventoryService()
        self.payment = PaymentService()
        self.shipping = ShippingService()

    def place_order(self, item_id, user_id, address):
        self.inventory.reserve(item_id)
        self.payment.charge(user_id, item_id)
        self.shipping.schedule(item_id, address)
        return "Order placed"

# Client only talks to the facade
facade = OrderFacade()
facade.place_order("item-1", "user-42", "123 Main St")

Proxy

An object that stands in for another object and controls access to it. It can add lazy loading, access control, logging, or caching without changing the real object.

class LazyImage:
    def __init__(self, filename):
        self._filename = filename
        self._image = None

    def draw(self):
        if self._image is None:
            print(f"Loading {self._filename}...")
            self._image = load_from_disk(self._filename)
        self._image.render()

# Expensive load happens only when draw() is first called

Together, these patterns help you structure Python code for clarity, reuse, and easier maintenance.

← Back to concepts