Observer Design Pattern

Hey Mr. Subject,
we the observers want to add bells & whistles around what you do,
please share your events with us!

Even though being simple, it is a powerful behavioral design pattern to build highly customizable and re-usable components.

In order to understand the power of this pattern, the simplest example is to think of a button. Even if it notifies a few basic events such onClick, hover etc; we can customize it for doing so many different things.

From a processing module point of view, the common example are the listeners. Because of these listeners, we can keep our core processing module simple, re-usable and highly customizable. The listeners here are nothing but the observers who add the bells and whistles around the core process.

How does the pattern work?

It basically tells you to share what is happening at your end as events, so that the Observers can add features around it externally.

The pattern basically consists of a subject and a set of observers monitoring the events happening at the subject.

The events could be representing some processing status, error conditions or a change of state etc. around which we want to customize our action. The set of observers allow us to build these the actions, whereas the subject take care of notifying these events.

The diagram below shows the UML of a sample observer implementation.

A Sample Observer Implementation

 

Part 1: Subject

The EventSource is the subject here.

It includes features to add or remove observers and, also, a method to notify the events to the list of observers.

package spectutz.dp.behavior.observer;

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

public class EventSource {
	
	//Methods to add and notify observers.......
    private final List<Observer> observers = new ArrayList<>();
  
    public void notifyObservers(String event) {
    	System.out.println("\nnotifyObservers() : "+event);//Update all observers
        observers.forEach(observer -> observer.update(event)); 
    }
  
    public void addObserver(Observer observer) {
        observers.add(observer);
    }  
    
    //Source of events
	public void doProcess() {
		Random randomGenerator = new Random();
		//Some event while processing.... 
		String eventX ="EventX: Value = "+100+randomGenerator.nextInt(10);
		notifyObservers(eventX);
		
		String eventY ="EventY: Value = "+100+randomGenerator.nextInt(10);
		notifyObservers(eventY);
	}

	
}

Part 2: Observers

The observers could be implementing an interface that accept the event, so that the subject can use it for sending the events. Here we are using an Observer interface with an update method to accept the event.

package spectutz.dp.behavior.observer;

public class ObserverOne implements Observer {

	public void update(String event) {
		System.out.println("ObserverOne processing event :"+event);
	}

}
package spectutz.dp.behavior.observer;

public class ObserverTwo implements Observer {

	public void update(String event) {
		System.out.println("ObserverTwo processing event :"+event);
	}

}
package spectutz.dp.behavior.observer;

public interface Observer {
	void update(String event);
}

Testing the Pattern

The below demo code adds two observers to the EvenSource object, the subject.

When we call the doProcess() method, the output shows how it updates the events to its observers.

package spectutz.dp.behavior.observer;

public class ObserverPatternDemo {
    public static void main(String[] args) {
        EventSource eventSource = new EventSource();
        
        //Adding two observers 
        eventSource.addObserver(new ObserverOne());
        eventSource.addObserver(new ObserverTwo());
        
        //Do some processing that generate events 
        eventSource.doProcess();
    }
}

/* Console Output : 

notifyObservers() : EventX: Value = 1001
ObserverOne processing event :EventX: Value = 1001
ObserverTwo processing event :EventX: Value = 1001

notifyObservers() : EventY: Value = 1005
ObserverOne processing event :EventY: Value = 1005
ObserverTwo processing event :EventY: Value = 1005

*/

Summary

To summarize, the observer pattern help us build highly customizable and re-usable components using event-driven approach.

The Subject can decided the points of customization by generating and sending the events to its observers. Whereas the Observers can use these events to add custom actions around it.

The events can represent a change of state in the subject or points of extension in a process inside the subject.