Skip to content

Exploring the Observer Design Pattern

Posted on:September 16, 2022 at 01:13 PM

The Observer design pattern is a crucial tool in a software developer’s toolkit. It addresses the challenge of efficiently communicating changes or updates within a system. In this blog post, we will delve into the Observer pattern, discussing its significance and demonstrating its implementation.

Table of contents

Open Table of contents

Sections

The Need for the Observer Pattern

In software development, we often encounter situations where a change in one part of the system needs to be communicated to other parts. This is especially true in scenarios where multiple components or modules need to stay in sync with the changes occurring in a particular element. The Observer pattern provides an elegant solution to this problem by establishing a subscription mechanism.

The Visual Representation

To illustrate the essence of the Observer pattern, let’s consider a scenario where we have a time-intensive process on one side, and multiple components waiting for the result of this process on the other side. Instead of the components polling for the result constantly, the Observer pattern introduces a registry in between, allowing the components to subscribe for updates. Once the result is ready, the registry notifies all the subscribed components.

observerGraphicProblem

observerGraphicSolution

Applying the Observer Design Pattern

Now, let’s take a look at a simple implementation of the Observer pattern in Java.

Event Listener Interface

public interface EventListener {
    void notify(String eventType, String file);
}

Event Manager

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class EventManager {
    private Map<String, List<EventListener>> listeners = new HashMap<>();

    public EventManager(String... operations) {
        for (String operation : operations) {
            listeners.put(operation, new ArrayList<>());
        }
    }

    public void subscribe(String eventType, EventListener listener) {
        List<EventListener> users = listeners.get(eventType);
        users.add(listener);
    }

    public void unsubscribe(String eventType, EventListener listener) {
        List<EventListener> users = listeners.get(eventType);
        users.remove(listener);
    }

    public void notify(String eventType, String file) {
        List<EventListener> users = listeners.get(eventType);
        for (EventListener listener : users) {
            listener.notify(eventType, file);
        }
    }
}

Editor

public class Editor {
    public EventManager events;
    private String file;

    public Editor() {
        this.events = new EventManager("open", "save");
    }

    public void openFile(String file) {
        this.file = file;
        events.notify("open", file);
    }

    public void saveFile() {
        if (this.file != null) {
            events.notify("save", this.file);
        } else {
            System.out.println("Please open a file first.");
        }
    }
}

EmailNotificationListener

public class EmailNotificationListener implements EventListener {
    @Override
    public void notify(String eventType, String file) {
        System.out.println("EmailNotificationListener: " + eventType + " performed on file: " + file);
    }
}

LogOpenListener

public class LogOpenListener implements EventListener {
    @Override
    public void notify(String eventType, String file) {
        System.out.println("LogOpenListener: " + eventType + " performed on file: " + file);
    }
}

Client Code

public class Client {
    public static void main(String[] args) {
        Editor editor = new Editor();
        editor.events.subscribe("open", new EmailNotificationListener());
        editor.events.subscribe("save", new EmailNotificationListener());
        editor.events.subscribe("open", new LogOpenListener());

        editor.openFile("test.txt");
        editor.saveFile();
    }
}

Conclusion

The Observer design pattern is a powerful tool for achieving efficient communication between different parts of a system. By implementing this pattern, developers can ensure that changes in one part of the system are seamlessly communicated to other interested components. Exploring and effectively implementing the Observer pattern can lead to more modular, maintainable, and flexible software systems.

Happy coding!