Design Patterns

Notes and exercises for learning design patterns

View the Project on GitHub Claptar/design-patterns

Exercise 2: Multiple Decorators and Ordering

The scenario

The notification system from Exercise 1 has grown. There are now three notifier types:

class EmailNotifier(Notifier):
    def send(self, recipient: str, message: str) -> None:
        print(f"EMAIL to {recipient}: {message}")

class SmsNotifier(Notifier):
    def send(self, recipient: str, message: str) -> None:
        print(f"SMS to {recipient}: {message[:160]}")  # SMS has a 160-char limit

class PushNotifier(Notifier):
    def send(self, recipient: str, message: str) -> None:
        print(f"PUSH to {recipient}: {message[:100]}")  # push has a 100-char limit

And four cross-cutting behaviours have been requested:


Your task

Part A — implement the four decorators

Implement LoggingDecorator, RetryDecorator, RateLimitDecorator, and PrefixDecorator.

RetryDecorator constructor:

RetryDecorator(wrapped, max_retries=3)

It should print "Retry {n}/{max_retries}..." on each retry attempt.

RateLimitDecorator constructor:

RateLimitDecorator(wrapped, limit=5)

It should raise RateLimitExceeded when the limit is reached.

PrefixDecorator constructor:

PrefixDecorator(wrapped, prefix="[URGENT] ")

Part B — ordering matters

Consider these two compositions and predict the output for each before running them:

# Composition A
notifier_a = RateLimitDecorator(
    LoggingDecorator(EmailNotifier()),
    limit=5,
)

# Composition B
notifier_b = LoggingDecorator(
    RateLimitDecorator(EmailNotifier(), limit=5)
)

Call notifier_a.send("alice@example.com", "hello") six times on composition A, then the same on composition B.

Answer these questions:

  1. In which composition does logging still happen when the rate limit is exceeded?
  2. Why?
  3. Which composition would you use if you wanted to audit every attempted send, including ones that were blocked?
  4. Which would you use if you only wanted to log sends that actually went through?

Part C — a real-world composition

Build a notifier for a production alert system with these requirements:

Write the composition and explain why you chose the order you did.


Skeleton

See exercise2.py.


What to focus on


Exercise 1 · Solution 2 · Exercise 3