devnotes.

Strategy and Observer Patterns

Behavioral patterns deal with communication and responsibility distribution between objects.

Strategy defines a family of interchangeable algorithms, encapsulates each one, and lets them be swapped independently of the code that uses them — a direct, practical application of the Open/Closed Principle from the SOLID chapter.

interface DiscountStrategy {
    function apply(price): number
}

class NoDiscount implements DiscountStrategy {
    function apply(price) { return price }
}
class PercentageDiscount implements DiscountStrategy {
    function __construct(private percent) {}
    function apply(price) { return price * (1 - this.percent / 100) }
}
class FlatDiscount implements DiscountStrategy {
    function __construct(private amount) {}
    function apply(price) { return max(0, price - this.amount) }
}

class ShoppingCart {
    function __construct(private DiscountStrategy strategy) {}

    function checkout(price) {
        return this.strategy.apply(price)
    }
}

cart = new ShoppingCart(new PercentageDiscount(20))
print(cart.checkout(100)) // 80

Adding a new discount type means writing a new class, never touching ShoppingCart itself. This is essentially the Open/Closed Principle applied specifically to swappable behavior rather than swappable object creation (as Factory handles).

Observer defines a one-to-many relationship where, when one object (the subject) changes state, all its dependents (the observers) are automatically notified — the foundation of most event-driven systems, GUI frameworks, and reactive programming.

class NewsPublisher {
    private subscribers = []

    function subscribe(observer) { this.subscribers.push(observer) }

    function publish(article) {
        for subscriber in this.subscribers:
            subscriber.notify(article) // every subscriber is informed automatically
    }
}

class EmailSubscriber {
    function notify(article) { print("Emailing article: " + article) }
}
class SmsSubscriber {
    function notify(article) { print("Texting article: " + article) }
}

publisher = new NewsPublisher()
publisher.subscribe(new EmailSubscriber())
publisher.subscribe(new SmsSubscriber())
publisher.publish("Breaking News!") // both subscribers are notified

The NewsPublisher doesn't need to know anything specific about EmailSubscriber or SmsSubscriber beyond the shared notify() contract — new subscriber types can be added freely without modifying the publisher at all. This pattern underlies event listeners in web browsers, GUI button clicks, and pub/sub messaging systems used across countless real-world applications.

Strategy and Observer Design Patterns Explained | DevNotes