Example of the observer pattern
To illustrate how the observer pattern works, let's look at a simple example in Python. Suppose you have a weather station that measures the temperature, humidity, and pressure, and you want to display these measurements on different devices, such as a console, a mobile app, or a web page. You can use the observer pattern to achieve this, by defining the following classes:
# The subject interface
class WeatherStation:
def attach(self, observer):
# Add an observer to the list of observers
pass
def detach(self, observer):
# Remove an observer from the list of observers
pass
def notify(self):
# Notify all observers of the changes in the state
pass
# The observer interface
class WeatherDisplay:
def update(self, temperature, humidity, pressure):
# Update the display with the new measurements
pass
# The concrete subject class
class WeatherStationImpl(WeatherStation):
def __init__(self):
# Initialize the state and the list of observers
self.temperature = 0
self.humidity = 0
self.pressure = 0
self.observers = []
def attach(self, observer):
# Add an observer to the list of observers
self.observers.append(observer)
def detach(self, observer):
# Remove an observer from the list of observers
self.observers.remove(observer)
def notify(self):
# Notify all observers of the changes in the state
for observer in self.observers:
observer.update(self.temperature, self.humidity, self.pressure)
def set_measurements(self, temperature, humidity, pressure):
# Set the new measurements and notify the observers
self.temperature = temperature
self.humidity = humidity
self.pressure = pressure
self.notify()
# The concrete observer classes
class ConsoleDisplay(WeatherDisplay):
def update(self, temperature, humidity, pressure):
# Update the display with the new measurements
print(f"Console: Temperature = {temperature}, Humidity = {humidity}, Pressure = {pressure}")
class MobileDisplay(WeatherDisplay):
def update(self, temperature, humidity, pressure):
# Update the display with the new measurements
print(f"Mobile: Temperature = {temperature}, Humidity = {humidity}, Pressure = {pressure}")
class WebDisplay(WeatherDisplay):
def update(self, temperature, humidity, pressure):
# Update the display with the new measurements
print(f"Web: Temperature = {temperature}, Humidity = {humidity}, Pressure = {pressure}")
Now, you can create an instance of the WeatherStationImpl class and three instances of the concrete observer classes, and register them with the weather station:
# Create a weather station and three displays
weather_station = WeatherStationImpl()
console_display = ConsoleDisplay()
mobile_display = MobileDisplay()
web_display = WebDisplay()
# Register the displays with the weather station
weather_station.attach(console_display)
weather_station.attach(mobile_display)
weather_station.attach(web_display)
Then, you can change the measurements of the weather station and see how the displays update themselves automatically:
# Change the measurements of the weather station
weather_station.set_measurements(25, 60, 1010)
# Output:
# Console: Temperature = 25, Humidity = 60, Pressure = 1010
# Mobile: Temperature = 25, Humidity = 60, Pressure = 1010
# Web: Temperature = 25, Humidity = 60, Pressure = 1010
weather_station.set_measurements(30, 50, 1005)
# Output:
# Console: Temperature = 30, Humidity = 50, Pressure = 1005
# Mobile: Temperature = 30, Humidity = 50, Pressure = 1005
# Web: Temperature = 30, Humidity = 50, Pressure = 1005
As you can see, the observer pattern allows you to decouple the weather station from the displays, and to add or remove displays without affecting the weather station or other displays. It also enables you to react to the changes in the weather station's state in a flexible and consistent way.