Understanding and Implementing the Observer Pattern in Software Development
Published: 2024-04-25 15:20:43

## Introduction
The Observer pattern is a fundamental behavioral design pattern in software engineering that allows a subscription mechanism to notify multiple objects of any event that occurs to the object they are observing. This pattern is also known by various names such as *Observer*, *Publish-Subscription*, *Model-View*, *Event-Subscribe*, and *Listener*.

## Problem Scenario
Consider a scenario that involves two types of objects: a **Customer** object and a **Store** object. The customer is very interested in a specific brand of product, such as a new iPhone model, which will soon be available in the store. The customer could visit the store daily to check the availability of the product, but most of these trips would be in vain until the product is actually available. On the other hand, the store could send hundreds of emails to all customers every time a new product becomes available, saving customers unnecessary travel but at the same time, potentially annoying other customers not interested in the new products.

## Observer Pattern Solution
The Observer pattern addresses this problem by suggesting that you add a subscription mechanism to the notifier class so that individual objects can subscribe or unsubscribe from an event stream of that notifier.

### Real-World Example
A practical example of the Observer pattern is a system of interaction between customers and stores:

- **Notifier**: The store that sends notifications about product availability.
- **Subscriber**: The customer who receives updates about the products of their interest.

### Code Example

#### Definition of Classes
Here we define the 'Notifier' and 'Subscriber' classes. The 'Notifier' class manages a list of subscribers and notifies each of them when an event occurs. The 'Subscriber' class simply prints the message it receives.

The Notifier class acts as the publisher (or subject) in the Observer pattern. This class is responsible for keeping a list of their observers and notifying them when something important happens.

```python
class Notificador:
    def __init__(self):
        self._observadores = []

    def suscribir(self, observador):
        self._observadores.append(observador)

    def desuscribir(self, observador):
        self._observadores.remove(observador)

    def notificar(self, mensaje):
        for observador in self._observadores:
            observador.actualizar(mensaje)
```python

The Subscriber class represents the observers in this pattern. Each subscriber who wants to receive notifications from the Notifier must implement this class.

```python
class Suscriptor:
    def actualizar(self, mensaje):
        print(f"Actualización recibida: {mensaje}")
```python


### Instantiating Classes
Finally, let's look at how these components interact in a usage example.

```python
tienda = Notificador()
cliente = Suscriptor()
```python


### Customer subscription to store notifications
```python
tienda.suscribir(cliente)
```python

### Notification of the availability of a new product
```python
tienda.notificar("¡El nuevo modelo de iPhone ya está disponible!")
```python



### Advantages of the Observer Pattern

#### Weak Coupling
The notifier doesn't need to know anything about subscribers. This allows the components of the system to be more independent of each other, making it easier to maintain and evolve.

#### Dynamic Relationships
New subscribers can join or leave at any time without the notifier needing to change. This makes the system highly adaptable and scalable to future changes.

## Conclusion

The Observer pattern is particularly useful in situations where an object needs to notify other objects about changes in its state, without knowing who these objects are. It promotes a clear separation of concerns and a well-organized architecture in applications.

By implementing the Observer pattern, software developers can create flexible, maintainable code that efficiently handles dynamic relationships between objects, making it ideal for managing complex dependencies in software applications.