Behavioral Design Patterns

=====================================

Behavioral design patterns are a class of structural and behavioral design patterns that focus on how objects interact with each other, rather than the structure or attributes of those objects. These patterns describe specific ways to solve problems by defining how to create classes that can adapt to different scenarios.

1. Strategy Pattern


The strategy pattern is a behavioral design pattern that allows clients to choose which strategy to use at runtime without changing the object itself.

Description

The strategy pattern defines an interface for strategies and concrete classes that implement these interfaces. A client of this pattern uses the strategy to select the desired behavior.

Example Use Case

Suppose you have a financial application where users can choose between different payment methods (credit card, PayPal, bank transfer). The strategy pattern would be used to define an interface for each method and implement it using concrete classes.

Code Example

from abc import ABC, abstractmethod

# Strategy Interface
class PaymentStrategy(ABC):
    @abstractmethod
    def pay(self, amount):
        pass

# Concrete Strategies
class CreditCard(PaymentStrategy):
    def __init__(self, card_number, expiration_date, CVV):
        self.card_number = card_number
        self.expiration_date = expiration_date
        self.CVV = CVV

    def pay(self, amount):
        print(f"Paying ${amount} using credit card {self.card_number}")

class PayPal(PaymentStrategy):
    def __init__(self, email):
        self.email = email

    def pay(self, amount):
        print(f"Paying ${amount} using PayPal account {self.email}")

# Client Code
class FinancialApplication:
    def __init__(self, payment_strategy):
        self.payment_strategy = payment_strategy

    def make_payment(self, amount):
        self.payment_strategy.pay(amount)

# Usage
credit_card = CreditCard("1234-5678-9012-3456", "12/2025", "123")
paypal = PayPal("example@example.com")

financial_app = FinancialApplication(credit_card)
financial_app.make_payment(100)

2. State Pattern


The state pattern is a behavioral design pattern that defines a class as an object that can change its behavior when interacted with.

Description

The state pattern provides a way to encapsulate an object’s internal state and define how it should change over time. This allows for more flexibility in designing objects that require dynamic behavior.

Example Use Case

Suppose you have a vending machine that can be in different states (open, closed, or maintenance). The state pattern would allow you to simulate these states by defining classes with attributes and methods that represent the different states.

Code Example

from abc import ABC, abstractmethod

# State Interface
class VendingMachineState(ABC):
    @abstractmethod
    def display_message(self):
        pass

# Concrete States
class OpenVendingMachine(VendingMachineState):
    def __init__(self):
        self.is_open = True

    def display_message(self):
        print("Vending machine is open")

class ClosedVendingMachine(VendingMachineState):
    def __init__(self):
        self.is_open = False

    def display_message(self):
        print("Vending machine is closed")

# Context Class
class VendingMachine:
    def __init__(self, state=None):
        self.state = state if state else OpenVendingMachine()

    def open(self):
        self.state = OpenVendingMachine()
        print("Opening vending machine")
        return self

    def close(self):
        self.state = ClosedVendingMachine()
        print("Closing vending machine")

# Client Code
vending_machine = VendingMachine()
vending_machine.open().close()

3. Observer Pattern


The observer pattern is a behavioral design pattern that defines an object as an event source and allows other objects to be notified when the state of an object changes.

Description

The observer pattern provides a way for clients to discover and respond to changes in an object’s behavior without having direct access to its implementation. This allows for more flexibility in designing applications with dynamic data.

Example Use Case

Suppose you have a weather service that notifies users when the temperature or precipitation levels change. The observer pattern would be used to define classes for the weather service and the subscribers (users) who want to receive notifications.

Code Example

from abc import ABC, abstractmethod

# Subject Interface
class WeatherService(ABC):
    @abstractmethod
    def notify(self):
        pass

# Concrete Subjects
class TemperatureWeatherService(WeatherService):
    def __init__(self, temperature):
        self.temperature = temperature

    def notify(self):
        print(f"Temperature is {self.temperature} degrees Celsius")

class PrecipitationWeatherService(WeatherService):
    def __init__(self, precipitation):
        self.precipitation = precipitation

    def notify(self):
        print(f"Precipitation is {self.precipitation} mm")

# Subject
class WeatherStation:
    def __init__(self):
        self.weather_services = []

    def add_weather_service(self, weather_service):
        self.weather_services.append(weather_service)

    def notify_subscribers(self):
        for service in self.weather_services:
            service.notify()

# Client Code
weather_station = WeatherStation()
temperature_service = TemperatureWeatherService(25)
precipitation_service = PrecipitationWeatherService(10)
weather_station.add_weather_service(temperature_service)
weather_station.add_weather_service(precipitation_service)

weather_station.notify_subscribers()

4. Model-View-Presenter (MVP) Pattern


The MVP pattern is a behavioral design pattern that separates the model, view, and presenter into distinct layers to improve testability and maintainability.

Description

The MVP pattern provides a way for developers to separate concerns by defining classes that represent each layer of an application. The model represents the data, the view represents the user interface, and the presenter handles business logic.

Example Use Case

Suppose you have a banking system with a user interface (GUI) where users can log in or register. The MVP pattern would be used to define separate classes for the model, view, and presenter.

Code Example

from abc import ABC, abstractmethod

# Model Interface
class UserModel(ABC):
    @abstractmethod
    def get_user_id(self):
        pass

# Concrete Models
class RegisteredUser(UserModel):
    def __init__(self, user_id):
        self.user_id = user_id

    def get_user_id(self):
        return self.user_id

# View Interface
class GUIView(ABC):
    @abstractmethod
    def render(self):
        pass

# Concrete Views
class LoginGUIView(GUIView):
    def render(self):
        print("Login GUI")

# Presenter Interface
class UserPresenter(ABC):
    @abstractmethod
    def login(self, user_id):
        pass

# Concrete Presenters
class RegisteredUserPresenter(UserPresenter):
    def __init__(self, model, view):
        self.model = model
        self.view = view

    def login(self, user_id):
        print(f"Logging in with user ID {user_id}")

# Client Code
model = RegisteredUser(1)
view = LoginGUIView()
presenter = RegisteredUserPresenter(model, view)

presenter.login(1)

Conclusion


Behavioral design patterns provide a way to structure code that is more modular, maintainable, and flexible. By using these patterns, developers can create applications with dynamic behavior that can adapt to changing requirements.

The behavioral design patterns discussed in this article are:

  • Strategy Pattern: Define an interface for strategies and concrete classes that implement it.
  • State Pattern: Represent different states of an object and provide methods to change the state.
  • Observer Pattern: Define an object as an event source and allow other objects to be notified when the state changes.
  • Model-View-Presenter (MVP) Pattern: Separate concerns by defining classes that represent each layer of an application.

By applying these patterns, developers can create more robust, scalable, and maintainable software systems.