Observer Design pattern

MaverickMaverick
5 min read

The Observer Design Pattern is a behavioral pattern used in software design. It establishes a one-to-many relationship between objects so that when one object changes state, all its dependents are notified and updated automatically.

Play Time

Alright, let's imagine you have a magical toy box. Every time you open the toy box, it does something totally cool, like singing a fun song, or maybe throwing out candy. Sounds exciting, right?

Now, imagine you have a bunch of friends who also want to know whenever your magical toy box is opened. Here's what you do:

You tell your friends, “Hey, whenever I open my toy box, I will shout ‘Toy Box Time!’ and you will know it's open and come see the cool things it does.”

What you just did is called the Observer Design Pattern! It's like having a special signal for your friends to know when something awesome is happening.

So here are the key components:

  1. Subject: The core object that holds some state and changes its state.

    Magical Toy Box is the thing that does this cool stuff. In a way it is the Observable.

  2. Observers: These are the objects that need to be notified when the subject’s state changes.

    You're the one who opens the toy box and notifies your friends.

  3. Publisher: The one who handles the state changes and sends notifications to the observers.

    Friends - They are waiting to hear "Toy Box Time!" and come running.

Step-by-Step Implementation

Let’s say this is happening in a school and you have a teacher. Teacher also wants to know when the box is opened, and how many times it has been opening.

  • Create the Subject Interface:
import java.util.ArrayList;
import java.util.List;

interface Subject {
    void registerObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
}
  • Create the Observer Interface:
interface Observer {
    void update();
}
  • Implement the Magical Toy Box (Observable Subject):
class MagicalToyBox implements Subject {
    private List<Observer> observers;
    private boolean isToyBoxOpened;
    private String color = "black";

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

    @Override
    public void registerObserver(Observer observer) {
        observers.add(observer);
    }

    @Override
    public void removeObserver(Observer observer) {
        observers.remove(observer);
    }

    @Override
    public void notifyObservers() {
        for (Observer observer : observers) {
            observer.update();
        }
    }

    public void setColor(String color) {
        this.color = color;
        System.out.println("Toy painted");
    }

    public String getColor(String color) {
        return this.color;
    }
}
  • Implement the Friends (Observers):
class Friend implements Observer {
    private String name;

    public Friend(String name) {
        this.name = name;
    }

    @Override
    public void update() {
        System.out.println(name + " is excited! Something cool is happening with the toy box!");
    }
}
  • Implement Teacher (Observer)
class Teacher implements Observer {
    private String name;
    private String subject;
    private MagicalToyBox box;

    public Teacher(String name, String subject, MagicalToyBox box) {
        this.name = name;
        this.subject = subject;
        this.box = box;
    }

    @Override
    public void update() {
        System.out.println(subject + " teacher " + name + " is happy that children are excited about the " + box.getcolor +" magical box!");

    }
}
  • Test the Implementation:
public class ObserverPatternDemo {
    public static void main(String[] args) {
        MagicalToyBox toyBox = new MagicalToyBox();

        Friend friend1 = new Friend("Alice");
        Friend friend2 = new Friend("Bob");
        Friend friend3 = new Friend("Charlie");
        Teacher teacher1 = new Teacher("Danny", "English", toyBox)
        Teacher teacher2 = new Teacher("Ferb", "Maths", toyBox)

        toyBox.registerObserver(friend1);
        toyBox.registerObserver(friend2);
        toyBox.registerObserver(teacher1);

        toyBox.openToyBox();  // This will notify friend1, friend2 and teacher1.

        System.out.println("");

        toyBox.registerObserver(friend3);
        toyBox.removeObserver(teacher1);
        toyBox.setColor("white");
        toyBox.registerObserver(teacher2);

        toyBox.openToyBox();  // This notify friend1, friend2, friend3 and teacher2.
    }
}

In simple terms:

  • Subject Interface: Defines methods for registering, removing, and notifying observers.

  • Observer Interface: Contains the update method which gets called when the subject's state changes.

  • MagicalToyBox Class: Implements the Subject interface and maintains a list of observers and notifies them when the toy box is opened by calling their update method.

  • Friend Class, Teacher Class: Implements the Observer interface and defines how each friend reacts when the toy box is opened.

When you run the ObserverPatternDemo class, it will output:

Toy Box Time!
Alice is excited! Something cool is happening with the toy box!
Bob is excited! Something cool is happening with the toy box!
English teacher Danny is happy that children are excited about the black magical box!

Alice is excited! Something cool is happening with the toy box!
Bob is excited! Something cool is happening with the toy box!
Charlie is excited! Something cool is happening with the toy box!
Maths teacher Ferb is happy that children are excited about the white magical box!

When to Use the Observer Design Pattern

You should consider using the Observer Design Pattern when:

  • You need to notify multiple objects about state changes in another object.

  • You want to decouple the subject from the observers, allowing the subject to operate independently of the observer classes.

  • The set of observers can change dynamically.

  • When the system requires multiple views of the same data (e.g., MVC architecture).

Why Use the Observer Design Pattern

Using the Observer Design Pattern helps in:

  • Maintaining consistency: Ensures all interested parties are updated consistently.

  • Decoupling components: Subjects don’t need to know the specifics of their observers, making the system more maintainable and flexible.

  • Dynamic handling of notifications: Observers can be added or removed without upsetting the subject.

Pros

  • Promotes loose coupling between subject and observers.

  • Makes the system more modular and easier to extend.

  • Facilitates easier maintenance and scalability.

Cons

  • May lead to performance issues if there are many observers or if updates are frequent.

  • Can become complex if observer lists are extensive or if observers have intricate dependencies.

Summary

The Observer Design Pattern establishes a one-to-many dependency, allowing objects (observers) to be notified and updated automatically when a subject changes its state. Key components include the Subject, Observer, Concrete Subject, and Concrete Observer. It's useful for real-time data updates and promotes loose coupling between the subject and observers.

0
Subscribe to my newsletter

Read articles from Maverick directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Maverick
Maverick