Notifying the World: A Dive into the Observer Design Pattern

Discover the observer design pattern in software development and how it helps to decouple objects and improve flexibility in complex applications.

Avinash Tingre
Javarevisited

--

Photo by Michał Jakubowski on Unsplash

In software development, it’s often necessary to design systems that respond to changes in the environment. For example, you might have a user interface that needs to update when the data it’s displaying changes, or a system that needs to send notifications when certain events occur.

The observer design pattern provides a way to manage these dynamic relationships between objects, so that when one object changes, the others are notified and updated automatically. It’s a popular design pattern that’s used in many different types of software systems, and it can help you build more robust and scalable systems.

How Does the Observer Design Pattern Work ❓

The observer design pattern consists of two types of objects: subjects and observers. Subjects are the objects that are being observed, and they maintain a list of observers. Observers are the objects that are notified when a change occurs in the subject.

When a change occurs in the subject, it calls a method on each of its observers to notify them of the change. Observers can then respond to the change as needed, for example by updating their state or taking some other action.

Why Use the Observer Design Pattern ❓

There are several benefits to using the observer design pattern:

  • Loose Coupling: The observer design pattern promotes loose coupling between objects, which makes it easier to maintain the code and make changes as needed.
  • Flexibility: Observers can be added or removed dynamically, so the system can be easily adapted to changing requirements.
  • Reusability: The observer design pattern makes it easier to reuse objects, since objects can be notified of changes without having to know the specifics of the subject that’s changing.

When to Use the Observer Design Pattern ❓

The observer design pattern is a useful design pattern for managing relationships between objects that need to respond to changes in the environment. It’s a common design pattern that’s used in many different types of software systems, including user interfaces, event-driven systems, and more.

If you’re building a software system that needs to respond to changes in the environment, and you want to promote loose coupling, flexibility, and reusability, the observer design pattern may be the right choice for you.

Common Use Cases:

  1. GUI Event Handling: The observer pattern can be used to handle user interactions and events in a graphical user interface.
  2. Stock Market Data: The observer pattern can be used to provide real-time updates of stock prices to multiple clients.
  3. News Feeds: The observer pattern can be used to distribute news feeds and updates to multiple clients.
  4. Social Media Notifications: The observer pattern can be used to provide real-time notifications to users about updates and new content on social media platforms.
  5. Logging and Debugging: The observer pattern can be used to log and debug changes to the state of an object in real-time.
  6. Chat Applications: The observer pattern can be used to distribute messages to multiple clients in a chat application.
  7. E-commerce Shopping Cart: The observer pattern can be used to notify users of changes in the availability of items in their shopping cart.
  8. Online Gaming: The observer pattern can be used to notify players of changes in game state and to distribute real-time updates in online multiplayer games.

How to implement observer design pattern ❓

Imagine you’re building a weather station application that displays weather updates to registered users. The weather station will be the subject, and the users will be the observers.

Here are the steps to implement the observer design pattern:

  1. Create an interface for the observer: The observer interface should have a method that updates the observer when the subject changes. In this example, the interface might be called WeatherObserver and the method might be called update(WeatherData weatherData).
  2. Create the subject: In this example, the subject is the weather station, and you might create a class called WeatherStation. The weather station should maintain a list of observers that are registered to receive updates, and provide methods to register and unregister observers.
  3. Implement the update method in the observer classes: In this example, you would have a class for each user that implements the WeatherObserver interface. The update method in each user class should display the updated weather data in a suitable format.
  4. Notify the observers when the subject changes: In this example, the weather station should update its state when new weather data is available. When this happens, the weather station should call the update method on each registered observer to notify them of the change.

Here’s an example implementation in Java:

// Observer interface
public interface WeatherObserver {
void update(WeatherData weatherData);
}

// Subject class
public class WeatherStation {
private List<WeatherObserver> observers;
private WeatherData weatherData;

public WeatherStation() {
observers = new ArrayList<>();
}

public void registerObserver(WeatherObserver observer) {
observers.add(observer);
}

public void removeObserver(WeatherObserver observer) {
observers.remove(observer);
}

public void setWeatherData(WeatherData weatherData) {
this.weatherData = weatherData;
notifyObservers();
}

private void notifyObservers() {
for (WeatherObserver observer : observers) {
observer.update(weatherData);
}
}
}

// Observer classes
public class User1 implements WeatherObserver {
@Override
public void update(WeatherData weatherData) {
System.out.println("User 1: " + weatherData);
}
}

public class User2 implements WeatherObserver {
@Override
public void update(WeatherData weatherData) {
System.out.println("User 2: " + weatherData);
}
}

// Weather data class
public class WeatherData {
private int temperature;
private int humidity;
private int pressure;

public WeatherData(int temperature, int humidity, int pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
}

public int getTemperature() {
return temperature;
}

public int getHumidity() {
return humidity;
}

public int getPressure() {
return pressure;
}

@Override
public String toString() {
return "WeatherData{" +
"temperature=" + temperature +
", humidity=" + humidity +
", pressure=" + pressure +
"}";
}
}

// Usage
public class Main {
public static void main(String[] args) {
WeatherStation weatherStation = new WeatherStation();

User1 user1 = new User1();
User2 user2 = new User2();

weatherStation.registerObserver(user1);
weatherStation.registerObserver(user2);

weatherStation.setWeatherData(new WeatherData(20, 70, 1010));
}
}

In this example, when the weather station updates its weather data, it notifies all registered observers (user1 and user2) through the update method, which displays the updated weather data. The observer design pattern provides a way for the weather station to notify its users of changes in a flexible and decoupled manner.

In Conclusion

The observer design pattern provides a way to manage dynamic relationships between objects in a flexible and scalable way. By using this design pattern, you can build software systems that are more robust, maintainable, and adaptable to changing requirements.

Thanks for reading, happy learning 🍀

--

--

Avinash Tingre
Javarevisited

Software Engineer. Jack of all trades; master of none :)