Notes and exercises for learning design patterns
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:
RateLimitExceeded if a per-instance limit is exceeded."[URGENT] " for critical alerts).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] ")
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:
Build a notifier for a production alert system with these requirements:
EmailNotifier."[ALERT] " to every message.Write the composition and explain why you chose the order you did.
See exercise2.py.