Design Patterns: A Beginner-Friendly Guide

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:

  • Easier to maintain
  • More flexible
  • Easier to test
  • Easier to extend
  • Better organized

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.

What Is a Design Pattern?

A design pattern describes:

  • A recurring problem
  • A reusable solution
  • When to apply it
  • Its pros and cons

Example:

Problem: Only one database connection manager should exist.

Solution: Use the Singleton pattern.

Categories of Design Patterns

Category Purpose
Creational Object creation
Structural Object composition
Behavioral Object interaction

There are 23 classic patterns.

Creational Patterns

These deal with creating objects.

1. Singleton Pattern

Problem: Only one object should exist.

Example

  • Database connection
  • Logger
  • Configuration manager
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:

  • Saves resources
  • Global access point

Disadvantages:

  • Harder to test
  • Can create hidden dependencies

2. Factory Pattern

Problem: Object creation logic becomes complicated.

Example

  • Creating different notification services.
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:

  • Payment gateways
  • Database drivers
  • UI components

3. Builder Pattern

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()
)

Structural Patterns

These organize classes and objects.

4. Adapter Pattern

Problem: Two incompatible interfaces must work together.

Example

  • Old payment API + new payment system.
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()

5. Decorator Pattern

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:

  • Authentication
  • Logging
  • Flask routes
  • Caching

6. Facade Pattern

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()

Behavioral Patterns

These control communication between objects.

7. Observer Pattern

Problem: One object changes and others need notification.

Examples:

  • Stock prices
  • Event systems
  • GUI buttons
  • Notifications
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!")

8. Strategy Pattern

Problem: Multiple algorithms can solve the same problem.

Examples:

  • payment methods.
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()

9. Command Pattern

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:

  • Undo/Redo
  • Task queues
  • Remote controls

SOLID Principles

Principle Meaning
S Single Responsibility
O Open/Closed
L Liskov Substitution
I Interface Segregation
D Dependency Inversion

When NOT to Use Design Patterns

  • The problem is simple
  • Code becomes more complex
  • Used only to appear “architectural”

E-commerce Checkout Example

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.

Real-World Examples

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.

Mini Project Idea

  • Strategy → payment methods
  • Factory → object creation
  • Observer → notifications
  • Builder → order creation
  • Decorator → logging