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.