Skip to main content

Python Design Patterns: Factory, Strategy, Observer, and More

Advanced30 min6 exercises120 XP
0/6 exercises

Design patterns are proven recipes for solving common software problems. Think of them like architectural blueprints -- you wouldn't redesign plumbing from scratch for every house. Patterns give your code a shared vocabulary so other developers instantly understand your intent.

Why Design Patterns in Python?

The classic "Gang of Four" patterns were written for Java and C++, languages with rigid type systems. Python's flexibility means many patterns are simpler -- or even unnecessary. A Strategy pattern that takes 20 lines in Java takes 3 in Python with first-class functions. We'll cover both the traditional OOP approach and the Pythonic shortcut for each pattern.


Factory Pattern: Let Someone Else Build It

The Factory pattern delegates object creation to a separate function or class. Instead of calling constructors directly, you ask a factory to give you the right object. This is perfect when you have a family of related classes and the choice depends on some input.

Python
Loading editor...

Strategy Pattern: Swap Behavior at Runtime

The Strategy pattern lets you change an algorithm without modifying the object that uses it. In Java, you'd create an interface and multiple implementing classes. In Python, you can just pass a function.

Traditional OOP Strategy
class SortStrategy:
    def sort(self, data): ...

class BubbleSort(SortStrategy):
    def sort(self, data):
        return sorted(data)

class ReverseSort(SortStrategy):
    def sort(self, data):
        return sorted(data, reverse=True)

class Sorter:
    def __init__(self, strategy):
        self.strategy = strategy
    def do_sort(self, data):
        return self.strategy.sort(data)
Pythonic Strategy (functions)
def sort_ascending(data):
    return sorted(data)

def sort_descending(data):
    return sorted(data, reverse=True)

def do_sort(data, strategy=sort_ascending):
    return strategy(data)

print(do_sort([3,1,2], sort_descending))

Observer Pattern: Notify When Things Change

The Observer pattern lets objects subscribe to events and get notified automatically when something changes. Think of it like a newsletter -- subscribers sign up, and the publisher sends updates to everyone on the list.

Python
Loading editor...

Singleton Pattern: One Instance Only

A Singleton ensures a class has only one instance. It's useful for shared resources like database connections or configuration objects. In Python, the simplest approach uses a module-level variable -- modules are natural singletons since they're only imported once.

Python
Loading editor...

Decorator Pattern (Not @decorator)

The Decorator design pattern wraps an object to add new behavior without modifying the original class. This is different from Python's @decorator syntax, though they share the same goal of extending functionality.

Python
Loading editor...

Template Method: Define the Skeleton

The Template Method defines the overall algorithm in a base class and lets subclasses override specific steps. The base class controls the flow; subclasses fill in the details.

Python
Loading editor...

Practice Exercises

Build a Shape Factory
Write Code

Create a factory function called create_shape that takes a string ('circle', 'square', or 'triangle') and returns an instance of the corresponding class. Each class should have an area() method:

  • Circle: area() returns 3.14 (simplified)
  • Square: area() returns 4 (side=2, area=2*2)
  • Triangle: area() returns 3.0 (base=3, height=2, area=0.5*3*2)
  • Raise ValueError for unknown shapes. Print the area of a circle.

    Loading editor...
    Strategy Pattern with Functions
    Write Code

    Create a function apply_discount(price, strategy) that takes a price and a discount strategy function, then returns the discounted price.

    Define two strategy functions:

  • half_off(price) -- returns price * 0.5
  • ten_percent(price) -- returns price * 0.9
  • Print the result of applying half_off to a price of 100, then print the result of applying ten_percent to 100.

    Loading editor...
    Build an Event System
    Write Code

    Create an EventEmitter class with:

  • on(event, callback) -- register a callback for an event
  • emit(event, *args) -- call all callbacks registered for that event, passing *args
  • Then:

    1. Create an emitter instance

    2. Register a callback for 'greet' that prints Hello, followed by the argument

    3. Register another callback for 'greet' that prints Hi there, followed by the argument

    4. Emit 'greet' with argument 'World'

    Loading editor...
    Predict Singleton Behavior
    Predict Output

    What does the following code print?

    class Config:
        _instance = None
        def __new__(cls):
            if cls._instance is None:
                cls._instance = super().__new__(cls)
                cls._instance.debug = False
            return cls._instance
    
    a = Config()
    a.debug = True
    b = Config()
    print(b.debug)
    print(a is b)
    Loading editor...
    Implement the Decorator Pattern
    Write Code

    Create a text processing pipeline using the Decorator pattern:

    1. PlainText class with __init__(self, text) and render() returning the text as-is

    2. BoldText class that wraps any text object and render() returns **<inner render>**

    3. UpperText class that wraps any text object and render() returns the inner render in uppercase

    Create a PlainText with 'hello', wrap it in BoldText, then wrap that in UpperText. Print the final render() result.

    Loading editor...
    Template Method Pattern
    Write Code

    Create a Report base class using the Template Method pattern:

  • generate() method that calls self.header(), self.body(), self.footer() in order
  • header() prints === Report ===
  • footer() prints === End ===
  • body() should raise NotImplementedError
  • Then create SalesReport(Report) that overrides body() to print Sales: $1000.

    Create a SalesReport instance and call generate().

    Loading editor...