Behavioral Patterns (Observer, Strategy, Command)

beginner
20 min

Behavioral Patterns (Observer, Strategy, Command)

Welcome to our deep dive into Behavioral Patterns! These are design patterns that focus on communication between objects, making your code more flexible, reusable, and maintainable. Today, we'll explore the Observer, Strategy, and Command patterns. Let's get started!

Observer šŸŽÆ

The Observer pattern allows multiple objects (called observers) to be notified automatically of any changes (events) in a single object (subject).

Why is it useful?

Imagine a social media app where users follow each other. Whenever a user posts something, all their followers should be notified. With Observer, you can easily handle this scenario by having the user (subject) notify their followers (observers) when a post is made.

How does it work?

  1. Define an interface for observers, with a method to update them when an event occurs.
  2. Define an interface for subjects, with a method to register and notify observers.
  3. Implement concrete observer and subject classes.

šŸ’” Pro Tip: Use an Observable class to manage the list of observers for a subject.

Code Example šŸ“

python
class Observer: def update(self, subject, message): pass class Subject: def __init__(self): self.observers = [] def register_observer(self, observer): self.observers.append(observer) def notify_observers(self, message): for observer in self.observers: observer.update(self, message) class ConcreteObserver(Observer): def update(self, subject, message): print(f"{self.__class__.__name__} got message: {message}") class ConcreteSubject(Subject): def notify_observers(self, message): for observer in self.observers: observer.update(self, message) if __name__ == "__main__": observer1 = ConcreteObserver() observer2 = ConcreteObserver() subject = ConcreteSubject() subject.register_observer(observer1) subject.register_observer(observer2) subject.notify_observers("Hello, Observers!")

Strategy šŸŽÆ

The Strategy pattern allows an algorithm to be selected at runtime and changes the behavior of an object. This can help reduce complexity in your code.

Why is it useful?

Imagine a shipping company that offers different shipping methods like standard, express, and overnight. Instead of having a single shipping class with many if-else statements, you can create a strategy interface and multiple concrete implementations for each shipping method.

How does it work?

  1. Define a strategy interface with a common method.
  2. Implement concrete strategies as separate classes.
  3. Use the concrete strategies interchangeably in client code.

šŸ’” Pro Tip: Use the Context class to encapsulate the strategy object and provide a uniform interface for clients.

Code Example šŸ“

python
from abc import ABC, abstractmethod class ShippingStrategy(ABC): @abstractmethod def calculate_cost(self, weight): pass class StandardShipping(ShippingStrategy): def calculate_cost(self, weight): return weight * 10 class ExpressShipping(ShippingStrategy): def calculate_cost(self, weight): return weight * 15 class OvernightShipping(ShippingStrategy): def calculate_cost(self, weight): return weight * 20 class Order: def __init__(self, shipping_strategy): self.shipping_strategy = shipping_strategy def calculate_total_cost(self, weight): return self.shipping_strategy.calculate_cost(weight) if __name__ == "__main__": order = Order(StandardShipping()) total_cost = order.calculate_total_cost(5) print(f"Total cost: {total_cost}")

Command šŸŽÆ

The Command pattern encapsulates a request as an object, allowing the request to be passed as a method argument, delayed execution, and support for undo/redo operations.

Why is it useful?

Imagine a text editor with an undo feature. The Command pattern allows you to record the actions (commands) as objects, making it easy to undo or redo them.

How does it work?

  1. Define a command interface with an execute method.
  2. Implement concrete command classes.
  3. Create an invoker to execute commands.

šŸ’” Pro Tip: Use a queue or stack to manage the commands for undo/redo functionality.

Code Example šŸ“

python
class Command: def execute(self): pass class ConcreteCommand(Command): def execute(self): print("ConcreteCommand executed") class Invoker: def execute_command(self, command): command.execute() if __name__ == "__main__": command = ConcreteCommand() invoker = Invoker() invoker.execute_command(command)
Quick Quiz
Question 1 of 1

What is the main purpose of the Observer pattern?

Now that you've learned about the Observer, Strategy, and Command patterns, you're one step closer to becoming a master of software engineering! Keep practicing, and happy coding! šŸš€šŸ’»šŸŒŸ