Menu Close

Object Oriented Design Pattern: Observer

observer design pattern

Introduction

The Observer Design pattern is a behavioral software design pattern. Personally, I first learned this pattern while working with AWT and button listeners. It allows one object, the subject, to notify other objects, called observers, about specific events. In this methodology, also known as the publisher-subscriber model, the publisher triggers events, and only the subscribers that registered with it receive those notifications. Furthermore, note that listener/observer/subscriber all refer to the same object and so do subject and publisher.

Use Cases

  • You want to have an object that can notify subscribers when something happens.
  • You want to be able to have objects listen to an event of another object.
  • You want to decouple the publisher and subscribers.

Pitfalls

  • A maze of publishers and subscribers makes it very complex to determine which subscribers subscribed to which publishers.
  • Ghost listeners, if you forget to unsubscribe a listener that is no longer necessary.

Java Example

I tried to create a simple example that is immediately clear to understand the pattern. Therefore let’s just have a Publisher object that will send events to Listeners. In the Main class, the publisher.triggerEvent() is uses to simulate something like an UI event like a button click.

Main

package com.chosengambit.designpatterns;

import com.chosengambit.observer.Listener;
import com.chosengambit.observer.Publisher;

public class Main {
    
    public static void main(String[] args) {
        
        // Create publisher/subject
        Publisher publisher = new Publisher();
        
        // Triggering an event without any listeners
        publisher.triggerEvent();
        
        // Create listener/subscriber/observer
        Listener observer = new Listener();
        
        // Add the listener to the publisher
        publisher.addListener(observer); 
        
        // Events are now listend to
        publisher.triggerEvent();
        publisher.triggerEvent();
        
        // Remove the listener
        publisher.removeListener(observer);
        
        // Event happens without listener again
        publisher.triggerEvent();
    }
}
Java

Interfaces

package com.chosengambit.observer;

public interface IPublisher {
    
    public void addListener(IListener listener);
    public void removeListener(IListener listener);
    public void notifyObservers();    
}
Java

package com.chosengambit.observer;

/**
 * Listeners, a.k.a. Subscribers, Observers
 */
public interface IListener {

    public void eventOccured(String eventType, String message);
}
Java

Publisher

The publisher is an object that can notify listeners about events. Often these classes listen to user input such as a button click and shares the click event to any listener object that wants to know about it. In this case we just use the triggerEvent() method to notify the observers.

package com.chosengambit.observer;

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

public class Publisher implements IPublisher {

    private final List<IListener> listeners = new ArrayList<>();

    @Override
    public void addListener(IListener listener) {
        listeners.add(listener);
    }

    @Override
    public void removeListener(IListener listener) {
        listeners.remove(listener);
    }

    @Override
    public void notifyObservers() {
        listeners.stream().forEach(listener -> listener.eventOccured("DefaultEvent", "Default event triggered."));
    }
    
    public void triggerEvent() {
        System.out.println("An event was triggered!");
        notifyObservers();
    }
}
Java

Listener

An implementation of a listener. Any class that implements the IListener interface can be added as a listener to the Publisher.

package com.chosengambit.observer;

public class Listener implements IListener {

    @Override
    public void eventOccured(String eventType, String message) {        
        String result = String.format("%s listened to event %s: %s ", this.toString(), eventType, message);
        
        System.out.println(result);        
    }       
}
Java

Output

Running the code triggers the event four times, while two times the listener got notified of the event.

An event was triggered!
An event was triggered!
com.chosengambit.observer.Listener@1be6f5c3 listened to event DefaultEvent: Default event triggered.
An event was triggered!
com.chosengambit.observer.Listener@1be6f5c3 listened to event DefaultEvent: Default event triggered.
An event was triggered!

Class Diagram

In the example code the listenToEvent method is named eventOccured.

Class Diagram: Observer Design Pattern

Conclusion

The Observer Design Pattern is all about subscribing and unsubscribing Listeners to Publishers that notify these Listeners about events that (might) happen. Publishers can function without Listeners and Listeners can function without Publishers as well. Each class could be made into a listener. With this simplified piece of code I hope to have made the core of the pattern clear in one take.

References

Freeman, E., Bates, B., & Sierra, K. (2004). Head first design patterns.

Gamma, E., Helm, R., Johnson, R., & Vlissides, J. (1994). Design Patterns: Elements of Reusable Object-Oriented Software.

Wikipedia contributors. (2024). Software design pattern. Wikipedia. https://en.wikipedia.org/wiki/Software_design_pattern

Related Posts