Latticework

Command Palette

Search for a command to run...

Design Patterns

Behavioral Patterns

18 min

Explanation

Behavioral patterns are about how objects communicate and share responsibility. The Strategy pattern makes an algorithm swappable: instead of hardcoding ONE way to do something, a class holds a reference to a function (or object) that defines the behavior, and calls it generically:

class Discounter:
    def __init__(self, strategy):
        self.strategy = strategy   # a function, chosen by the caller

    def apply(self, price):
        return self.strategy(price)

ten_percent_off = Discounter(lambda p: p * 0.9)
flat_ten_off = Discounter(lambda p: p - 10)

print(ten_percent_off.apply(100))   # 90.0
print(flat_ten_off.apply(100))       # 90

Same Discounter class, completely different discount logic — swapped in from outside, with zero changes to Discounter itself. That's exactly the same idea as the Strategy pattern in the Optimization course's gradient descent functions accepting f_prime/grad_f as arguments.

Try it

Adding a new discount strategy never requires touching the Discounter class -- you just define a new function and pass it in. That's the flexibility Strategy buys you.

Loading editor…
Explanation

The Observer pattern lets one object (the "subject") notify a list of other objects ("observers") whenever something happens, without needing to know anything about them beyond "they're callable":

class EventEmitter:
    def __init__(self):
        self.subscribers = []

    def subscribe(self, callback):
        self.subscribers.append(callback)
        return self

    def emit(self, value):
        return [cb(value) for cb in self.subscribers]

emitter = EventEmitter()
emitter.subscribe(lambda x: print(f"logger: {x}"))
emitter.subscribe(lambda x: x * 2)
emitter.emit(21)

This is the exact pattern behind UI event listeners, pub/sub messaging systems, and reactive frameworks — the emitter doesn't know or care what its subscribers DO, it just calls them all when something happens.

Exercise

Write `Discounter.apply(self, price)` (the Strategy pattern): `self.strategy` is a function; call it with `price` and return the result.

Exercise

Write `EventEmitter.emit(self, value)` (the Observer pattern): call every subscribed callback in `self.subscribers` with `value`, and return a list of their return values, in subscription order.

Quiz

What problem does the Strategy pattern solve?

Checkpoint

You can implement the Strategy pattern to make behavior swappable at runtime, and the Observer pattern to notify multiple subscribers of an event.