Observer Design Pattern

What is an Observer Design Pattern

The Observer design pattern is a behavioral software design pattern that is used to establish a one-to-many dependency between objects. In this pattern, when one object (known as the subject) changes its state, all its dependents (known as observers) are automatically notified and updated accordingly.

It allows multiple objects to be notified of changes in the state of another object without requiring them to know the specifics of the subject.

The Observer Design Pattern is a way for one object, known as the subject, to send updates to multiple other objects, known as observers, when it changes.

An example of this pattern in real life could be a weather service sending updates to different weather apps when the weather changes. The weather service is the subject and the weather apps are the observers.


Explanation

The Observer Design Pattern is a way for one object, known as the subject, to notify multiple other objects, known as observers, about changes in its state. The subject maintains a list of its observers and notifies them when its state changes.

In observer design pattern multiple observer objects registers with a subject for change notification. When the state of subject changes, it notifies the observers.

Objects that listen or watch for change are called observers and the object that is being watched for is called subject.

Pattern involved is also called as publish-subscribe pattern.

  • Subject provides interface for observers to register and unregister themselves with the
    subject.
  • Subject knows who its subscribers are.
  • Multiple observers can subscribe for notifications.
  • Subject publishes the notifications.
  • Subject just sends the notification saying the state has changed. It does not pass any state information.
  • Once the notification is received from subject, observers call the subject and get data
    that is changed.

In some implementations, along with the notification, state is also passed so that the observer need not query back to know the status. It is better not to do this way.

There are 4 participants in the Observer pattern:

  • Subject, which is used to register observers. Objects use this interface to register as
    observers and also to remove themselves from being observers.
  • Observer defines an updating interface for objects that should be notified of changes in a subject. All observers need to implement the Observer interface. This interface has a method update (), which gets called when the Subject’s state changes.
  • ConcreteSubject, stores the state of interest to ConcreteObserver objects. It sends a
    notification to its observers when its state changes. A concrete subject always implements the Subject interface. The notifyObservers () method is used to update all the current observers whenever the state changes.
  • ConcreateObserver maintains a reference to a ConcreteSubject object and implements the Observer interface. Each observer registers with a concrete subject to receive updates.

This pattern can be useful in situations where multiple objects need to stay updated with the state of a single object, and the objects do not need to interact directly with each other.

This pattern is widely used in many different applications, such as GUI applications, event-driven systems, and reactive programming. It is a fundamental pattern that can help you to design more flexible and scalable systems.

Benefits

The Observer design pattern offers several benefits, making it a valuable tool in software development. Here are some of the key benefits of using the Observer pattern:

  1. Loose coupling: The Observer pattern promotes loose coupling between the subject and its observers. Observers don’t need to know the specifics of the subject’s implementation; they only rely on the common Observer interface. This reduces the dependencies between classes, making the code more maintainable and flexible.
  2. Extensibility: Introducing new observers becomes easy. You can create new observer classes without modifying the subject. This makes it simple to add new functionalities to a system without affecting existing code.
  3. Reusability: Observers can be reused in different contexts with different subjects. This reusability is possible because of the separation of concerns provided by the Observer pattern.
  4. Event handling: The Observer pattern is commonly used in event-driven systems. When an event occurs, the subject notifies its observers, and they can respond to the event accordingly. This facilitates a clean and efficient way of handling events in the application.
  5. Decoupled UI components: In graphical user interfaces (GUIs), the Observer pattern is often used to ensure that the UI components are decoupled from the underlying data. UI components can register themselves as observers to receive updates when the data changes, allowing for a responsive and synchronized user interface.
  6. Real-time updates: The Observer pattern is useful in scenarios where real-time updates are needed. For example, in chat applications or stock market monitoring systems, observers can be notified immediately when new messages or stock prices arrive.
  7. Maintainability: By separating the concerns of the subject and its observers, the codebase becomes easier to maintain. Changes to one part of the system are less likely to affect other parts, reducing the risk of introducing bugs and making it easier to refactor or add new features.
  8. Scalability: The Observer pattern enables a scalable architecture by allowing multiple observers to be added or removed dynamically at runtime. This is particularly valuable in large applications where different components need to react to changes in a subject independently.

Overall, the Observer design pattern provides a powerful mechanism for building flexible and decoupled systems, enabling better code organization and easier maintenance. It is widely used in various domains, including user interfaces, event handling, and real-time applications.

Implementation

Let us take a blog and subscriber example for observer design pattern sample implementation. Assume that there is a blog and users register to that blog for update.
When a new article is posted in the blog, it will send update to the registered users saying a new article is posted. Then the user will access the blog and read the new article posted. In this example, blog is the subject and user is the observer.

Subject interface

package com.rndayala.designpatterns.observable;

// Subject interface
public interface Subject {
	void registerObserver(Observer observer);
	void unregisterObserver(Observer observer);
	void notifyObservers();
	Object getUpdate();
}

Observer interface

package com.rndayala.designpatterns.observable;

// Observer interface
public interface Observer {
	void update(Subject subject);

}

Concrete Subject implementation – Blog class

package com.rndayala.designpatterns.observable;

import java.util.ArrayList;
import java.util.List;

// Concrete Subject class 
public class Blog implements Subject {
	// Concrete Subject maintains list of observers
	private List<Observer> observers = null;
	// this instance variable maintains the state of Concrete subject
	private String blogContent;
	
	public Blog() {
		System.out.println("Initializing subject(blog)..");
		this.observers = new ArrayList<Observer>();
		blogContent = "";
	}
	
	@Override
	public void registerObserver(Observer observer) {
		System.out.println("registering an observer!");
		observers.add(observer);		
	}
	
	@Override
	public void unregisterObserver(Observer observer) {
		System.out.println("un-registering an observer!");
		observers.remove(observer);
	}

        // when the state of subject changes, we need to notify observers
	public void postNewArticle(String data) {
		blogContent = data;
		notifyObservers();
	}
	
	@Override
	public void notifyObservers() {
                // for each observer, call the update() method allowing observer to react to the change in subject state		
		for (Observer observer : observers) {
			observer.update(this);
			System.out.println("Observer notified!!");
		}
	}
	
	@Override
	public Object getUpdate() {
		return blogContent;
	}
	
        // method to return the list of observers registered with the subject
	public List<Observer> getObserversList() {
		return observers;
	}
}

Concrete observer implementation – User class

package com.rndayala.designpatterns.observable;

public class User implements Observer {
	private Object article;


        // on invocation of update() method, the observer will update its own state.
	@Override
	public void update(Subject subject) {
		article = subject.getUpdate();		
	}

	public Object getArticle() {
		return article;
	}

}

Client code – Demo program

package com.rndayala.designpatterns.observable;

import java.util.List;

public class Demo {
	public static void main(String[] args) {
		Blog blog = new Blog();
		User user1 = new User();
		User user2 = new User();
		List<Observer> list = null;
		
		blog.registerObserver(user1);
		blog.registerObserver(user2);
		
                // change the state of subject by posting a new article
		blog.postNewArticle("Observer pattern Explained!");

		list = blog.getObserversList();
		
		for(Observer observer : list) {
			System.out.println("Get content : " + ((User)observer).getArticle());
		}		
		
                // remove an observer		
		blog.unregisterObserver(user2);

		blog.postNewArticle("Singleton pattern Explained!");

		list = blog.getObserversList();
		
		for(Observer observer : list) {
			System.out.println("Get content : " + ((User)observer).getArticle());
		}

	}
}

Output :

Initializing subject(blog)..
registering an observer!
registering an observer!
Observer notified!!
Observer notified!!
Get content : Observer pattern Explained!
Get content : Observer pattern Explained!
un-registering an observer!
Observer notified!!
Get content : Singleton pattern Explained!

When the state of the subject changes, it calls the notifyObservers() method which in turn calls the update method on each of its observers, allowing them to react to the change in subject’s state.


Use cases

The Observer design pattern is typically used in situations where there is a one-to-many relationship between objects and when changes in one object need to be reflected in other objects.

Some common use cases of the Observer pattern are:

1. Implementing a model-view-controller architecture where changes in the model are notified to the views.

2. Implementing event-driven systems, such as user interfaces, where changes in one component trigger updates in other components.

3. Implementing a publish-subscribe system where events are published to multiple subscribers.

4. Implementing a logging system, where changes in the log data need to be notified to multiple log listeners.

5. Implementing a stock ticker system, where changes in the stock prices need to be notified to multiple subscribers.

In all these use cases, the Observer pattern allows the objects to be loosely coupled, so that changes in one object don’t affect the other objects directly. Instead, the changes are notified to the objects that need to be updated.


Observer Design Pattern implementation using Weather station scenario

Here’s a Java code example of the Observer design pattern using the weather station scenario:

When the weather station’s temperature changes, it notifies all its attached observers (TemperatureDisplay and Fan). The TemperatureDisplay then prints the updated temperature, while the Fan turns on or off based on the temperature threshold.

import java.util.ArrayList;
import java.util.List;

// Observer interface
interface Observer {
    void update(int temperature);
}

// Subject
class WeatherStation {
    private List<Observer> observers = new ArrayList<>();
    private int temperature;

    public void attachObserver(Observer observer) {
        observers.add(observer);
    }

    public void detachObserver(Observer observer) {
        observers.remove(observer);
    }

    public void setTemperature(int temperature) {
        this.temperature = temperature;
        notifyObservers();
    }

    // here, while notifying observer, we are sending the state also
    private void notifyObservers() {
        for (Observer observer : observers) {
            observer.update(temperature);
        }
    }
}

// Concrete Observer
class TemperatureDisplay implements Observer {
    @Override
    public void update(int temperature) {
        System.out.println("Temperature Display: " + temperature + " degrees Celsius");
    }
}

// Concrete Observer
class Fan implements Observer {
    @Override
    public void update(int temperature) {
        if (temperature > 25) {
            System.out.println("Fan: Turning on the fan.");
        } else {
            System.out.println("Fan: Turning off the fan.");
        }
    }
}

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

        weatherStation.attachObserver(tempDisplay);
        weatherStation.attachObserver(fan);

        weatherStation.setTemperature(20);
        weatherStation.setTemperature(30);
    }
}

Output :

Temperature Display: 20 degrees Celsius
Fan: Turning off the fan.
Temperature Display: 30 degrees Celsius
Fan: Turning on the fan.

This example demonstrates how the WeatherStation subject notifies its attached observers (TemperatureDisplay and Fan) about changes in the temperature, and each observer reacts accordingly.

Leave a comment