DEV Community

Cover image for How the Observer Pattern Works: A Car Analogy
iAmSherif 💎
iAmSherif 💎

Posted on

How the Observer Pattern Works: A Car Analogy

The observer pattern is a behavioral design pattern that establishes a one-to-many relationship between objects, allowing multiple observers to monitor the state of a single object, referred to as the observable. These observers are automatically notified whenever significant events occur in the observable, and the observable maintains a list of its observers, ensuring they stay informed about any changes in its state.

Understanding Observer Pattern with a Car Example

Let's delve into the concept by considering the instrument panel of a car—the dashboard behind the steering wheel. This panel includes various gauges and lights, providing real-time updates to the driver about the vehicle's status.

Let's explain the definition of Observer pattern:

One-to-Many Relationship

The observer pattern defines a one-to-many relationship, where a single observable object has a connection with multiple observer objects. This relationship is evident in various scenarios:

  • Car and Its Components:

    • Observable: The car itself, capable of changing its state (e.g., speed, fuel level, temperature).
    • Observers: Various components within the car, such as the gear, fuel gauge, and speedometer, each updating itself based on the changes it observes.
  • Company and Staff Emails:

    • Observable: The company, responsible for sending out emails.
    • Observers: Numerous staff members, are automatically notified of any incoming emails.
  • Phone and Its Components:

    • Observable: The phone, with dynamic features like battery level and room brightness.
    • Observers: Components like battery level indicator and ambient light sensor, adjust phone brightness automatically.

Maintaining a List of Observers

In the observer pattern, the object being observed maintains a list of its observers. This list allows seamless communication between the observable and its observers:

  • Car and Its Components:

    • List of Observers: The car keeps track of components like the fuel gauge and speedometer, which observe its state.
  • Company and Staff Emails:

    • List of Observers: The company maintains a list of staff emails.
  • Phone and Its Components:

    • List of Observers: The phone maintains a list of apps or components, like a power button click counter app.

Notifying Observers of State Changes

The crucial aspect of the observer pattern is the automatic notification of observers when the observable's state changes:

  • Company and Staff Emails:

    • Notification: Staff members automatically receive notifications of any new emails from the company.
  • Car and Its Components:

    • Notification: Car components, such as the fuel gauge and speedometer, automatically update themselves in response to car state changes.
  • Phone and Its Components:

    • Notification: The phone adjusts its brightness automatically based on changes in room brightness.

How it works? Let's demonstrate this in code:

# Define the Car class as the observable
class Car:
    def __init__(self):
        # The observable maintains a list of observers which means it has a relationship with these observers
        self._observers: list = []
        self._fuel_level: int = 4 # (in litres)
        self._speed: int = 0

        # An observer can subscribe to the list
    def add(self, observer) -> None:
        self._observers.append(observer)

        # An observer can also unsubscribe from the list
    def remove(self, observer) -> None:
        self._observers.remove(observer)

        # All subscribed observers are notified whenever this method is invoked
    def notify_observers(self) -> None:
        for observer in self._observers:
            observer.update(self)

        # This accelerate method causes a change in the Car's state and it automatically notifies the observers of its changes
    def accelerate(self) -> None:
        self._speed += 1
        notify_observers()

        # This top_up method also causes a change in the Car's state and it automatically notifies the observers of its changes
    def top_up(self, litre) -> None:
        self._fuel_level += litre
        notify_observers()

        # The below methods return the value in fuel_level and speed respectively
    def get_fuel_level(self) -> int:
        return self._fuel_level

    def get_speed(self) -> int:
        return self._speed

Enter fullscreen mode Exit fullscreen mode

IObserver defines a signature for all observers to implement

from abc import ABC, abstractmethod

# Define the Observer interface
class IObserver(abc.ABC):

    @abstractmmethod
    def update(self):
        pass
Enter fullscreen mode Exit fullscreen mode

Let's define concrete observers

# Define concrete observers

class FuelGauge(IObserver):
    def __init__(self, car):
        self._fuel_level = 0
        self.car = car

    def update(self, car) -> None:
        self._fuel_level = car.get_fuel_level()

class Speedometer(IObserver) :
    def __init__(self, car):
        self._speed = 0
        self.car = car

    def update(self, car) -> None:
        self._speed =car.get_speed()

Enter fullscreen mode Exit fullscreen mode

Client class


if __name__ == 'main' : 
    # Create a car object
    car = Car()

    # Create speedometer and fuelGuage objects 
    speedometer: Speedometer = Speedometer(car)
    fuel_guage: FuelGauge = FuelGauge(car)

    # Add the observers to car object
    car.add(speedometer)
    car.add(fuel_gauge)

    # Change the car's state, observers are notified automatically about this change
    car.accelerate()
Enter fullscreen mode Exit fullscreen mode

Conclusion

In summary, the observer pattern facilitates a flexible and decoupled design, allowing objects to communicate without being tightly bound. By exploring the observer pattern through the example of a car, we gain insights into how observables and observers collaborate in a dynamic and efficient manner.

Follow on LinkedIn and Twitter for more on Design patterns:
click to follow on LinkedIn
click to follow on Twitter

Speedy emails, satisfied customers

Postmark Image

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay