Design patterns are proven solutions to common software design problems. They are not libraries or frameworks. Instead, they are reusable ideas that help developers write code that is:
Think of design patterns like architectural blueprints. An architect doesn't redesign doors and windows for every house; similarly, developers don't reinvent solutions to common problems.
A design pattern describes:
Example:
Problem: Only one database connection manager should exist.
Solution: Use the Singleton pattern.
| Category | Purpose |
|---|---|
| Creational | Object creation |
| Structural | Object composition |
| Behavioral | Object interaction |
There are 23 classic patterns.
These deal with creating objects.
Problem: Only one object should exist.
Example
class Database:
_instance = None
def __new__(cls):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
db1 = Database()
db2 = Database()
print(db1 is db2) # True
Advantages:
Disadvantages:
Problem: Object creation logic becomes complicated.
Example
class Email:
def send(self):
print("Email sent")
class SMS:
def send(self):
print("SMS sent")
class NotificationFactory:
@staticmethod
def create(notification_type):
if notification_type == "email":
return Email()
elif notification_type == "sms":
return SMS()
service = NotificationFactory.create("email")
service.send()
Real-world uses:
Problem: Objects require many optional parameters.
class Computer:
def __init__(self):
self.cpu = None
self.ram = None
self.storage = None
class ComputerBuilder:
def __init__(self):
self.computer = Computer()
def add_cpu(self, cpu):
self.computer.cpu = cpu
return self
def add_ram(self, ram):
self.computer.ram = ram
return self
def build(self):
return self.computer
pc = (
ComputerBuilder()
.add_cpu("Intel i9")
.add_ram("32GB")
.build()
)
These organize classes and objects.
Problem: Two incompatible interfaces must work together.
Example
class OldPrinter:
def old_print(self):
print("Printing")
class PrinterAdapter:
def __init__(self, printer):
self.printer = printer
def print(self):
self.printer.old_print()
adapter = PrinterAdapter(OldPrinter())
adapter.print()
Problem: Add functionality without changing existing code.
def uppercase(func):
def wrapper():
return func().upper()
return wrapper
@uppercase
def greet():
return "hello"
print(greet())
Used in:
Problem: Complex systems expose too many details.
class CPU:
def start(self):
print("CPU started")
class Memory:
def load(self):
print("Memory loaded")
class Computer:
def start(self):
CPU().start()
Memory().load()
pc = Computer()
pc.start()
These control communication between objects.
Problem: One object changes and others need notification.
Examples:
class Subscriber:
def update(self, message):
print(message)
class Channel:
def __init__(self):
self.subscribers = []
def subscribe(self, sub):
self.subscribers.append(sub)
def notify(self, msg):
for sub in self.subscribers:
sub.update(msg)
channel = Channel()
channel.subscribe(Subscriber())
channel.notify("New video uploaded!")
Problem: Multiple algorithms can solve the same problem.
Examples:
class CreditCard:
def pay(self):
print("Paid with card")
class PayPal:
def pay(self):
print("Paid with PayPal")
class Checkout:
def __init__(self, payment):
self.payment = payment
def process(self):
self.payment.pay()
Checkout(PayPal()).process()
Problem: Encapsulate actions as objects.
class Light:
def on(self):
print("Light ON")
class LightCommand:
def __init__(self, light):
self.light = light
def execute(self):
self.light.on()
cmd = LightCommand(Light())
cmd.execute()
Used in:
| Principle | Meaning |
|---|---|
| S | Single Responsibility |
| O | Open/Closed |
| L | Liskov Substitution |
| I | Interface Segregation |
| D | Dependency Inversion |
Imagine an online store checkout system. Modern applications often combine multiple design patterns to handle different responsibilities cleanly.
| Problem | Pattern |
|---|---|
| Multiple payment methods | Strategy |
| Create payment objects | Factory |
| Send notifications | Observer |
| Logging requests | Decorator |
| Build orders | Builder |
| Simplify APIs | Facade |
Modern applications rarely use a single design pattern. Instead, they combine multiple patterns together to build scalable, maintainable systems.
Design patterns are not just theoretical concepts. They are widely used in modern frameworks, libraries, and cloud systems.
| Technology | Pattern |
|---|---|
| Flask decorators | Decorator |
| Django signals | Observer |
| SQLAlchemy session | Singleton |
| React components | Composite |
| Logging frameworks | Singleton |
| Dependency Injection containers | Factory |
| AWS SDK clients | Builder |
| Payment gateways | Strategy |
These examples show how design patterns are embedded in everyday tools and frameworks used in production systems.