Observer design pattern

Observer Design Pattern Example

To understand observer design pattern, lets take an example:

Consider a scenario where you have a weather monitoring system. Different parts of your application need to be updated when the weather conditions change.

Challenges or difficulties while implementing this system without Observer Design Pattern

  • Components interested in weather updates would need direct references to the weather monitoring system, leading to tight coupling.
  • Adding or removing components that react to weather changes requires modifying the core weather monitoring system code, making it hard to maintain.

How Observer Pattern helps to solve above challenges?

The Observer Pattern facilitates the decoupling of the weather monitoring system from the components that are interested in weather updates (via interfaces). Every element can sign up as an observer, and observers are informed when the weather conditions change. The weather monitoring system is thus unaffected by the addition or removal of components.

ObserverPatternExample

Below is the code of above problem statement using Observer Pattern:

1. Subject

  • The “Subject" interface outlines the operations a subject (like “WeatherStation") should support.
  • "addObserver" and “removeObserver" are for managing the list of observers.
  • "notifyObservers" is for informing observers about changes.
public interface Subject {
    void addObserver(Observer observer);
    void removeObserver(Observer observer);
    void notifyObservers();
}

2. Observer

  • The “Observer" interface defines a contract for objects that want to be notified about changes in the subject (“WeatherStation" in this case).
  • It includes a method “update" that concrete observers must implement to receive and handle updates.
public interface Observer {
    void update(String weather);
}

3. ConcreteSubject(WeatherStation)

  • "WeatherStation" is the concrete subject implementing the “Subject" interface.
  • It maintains a list of observers (“observers") and provides methods to manage this list.
  • "notifyObservers" iterates through the observers and calls their “update" method, passing the current weather.
  • "setWeather" method updates the weather and notifies observers of the change.
import java.util.ArrayList;
import java.util.List;

public class WeatherStation implements Subject {
    private List<Observer> observers = new ArrayList<>();
    private String weather;

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

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

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

    public void setWeather(String newWeather) {
        this.weather = newWeather;
        notifyObservers();
    }
}

4. ConcreteObserver(PhoneDisplay)

  • "PhoneDisplay" is a concrete observer implementing the “Observer" interface.
  • It has a private field weather to store the latest weather.
  • The “update" method sets the new weather and calls the “display" method.
  • "display" prints the updated weather to the console.
public class PhoneDisplay implements Observer {
    private String weather;

    @Override
    public void update(String weather) {
        this.weather = weather;
        display();
    }

    private void display() {
        System.out.println("Phone Display: Weather updated - " + weather);
    }
}

5. ConcreteObserver(TVDisplay)

  • "TVDisplay" is another concrete observer similar to “PhoneDisplay".
  • It also implements the “Observer" interface, with a similar structure to “PhoneDisplay".
class TVDisplay implements Observer {
    private String weather;
 
    @Override
    public void update(String weather) {
        this.weather = weather;
        display();
    }
 
    private void display() {
        System.out.println("TV Display: Weather updated - " + weather);
    }
}

6. Usage

  • In “WeatherApp", a “WeatherStation" is created.
  • Two observers (“PhoneDisplay" and “TVDisplay") are registered with the weather station using “addObserver".
  • The “setWeather" method simulates a weather change to “Sunny,” triggering the “update" method in both observers.
  • The output shows how both concrete observers display the updated weather information.

public class WeatherApp {
    public static void main(String[] args) {
        WeatherStation weatherStation = new WeatherStation();

        Observer phoneDisplay = new PhoneDisplay();
        Observer tvDisplay = new TVDisplay();

        weatherStation.addObserver(phoneDisplay);
        weatherStation.addObserver(tvDisplay);

        // Simulating weather change
        weatherStation.setWeather("Sunny");

        // Output:
        // Phone Display: Weather updated - Sunny
        // TV Display: Weather updated - Sunny
    }
}


// Observer interface

interface Observer {

    void update(String state);

}


// Concrete Observer

class LightObserver implements Observer {

    private String name;


    LightObserver(String name) {

        this.name = name;

    }


    @Override

    public void update(String state) {

        System.out.println(name + " received update: Light is " + state);

    }

}


// Subject interface

import java.util.ArrayList;

import java.util.List;


interface Subject {

    void attach(Observer observer);

    void detach(Observer observer);

    void notifyObservers();

}


// Concrete Subject

class Light implements Subject {

    private List<Observer> observers = new ArrayList<>();

    private String state = "OFF";


    public void turnOn() {

        state = "ON";

        notifyObservers();

    }


    public void turnOff() {

        state = "OFF";

        notifyObservers();

    }


    @Override

    public void attach(Observer observer) {

        observers.add(observer);

    }


    @Override

    public void detach(Observer observer) {

        observers.remove(observer);

    }


    @Override

    public void notifyObservers() {

        for (Observer observer : observers) {

            observer.update(state);

        }

    }

}


// Client

public class ObserverPatternDemo {

    public static void main(String[] args) {

        Light light = new Light();

        Observer observer1 = new LightObserver("Observer 1");

        Observer observer2 = new LightObserver("Observer 2");

        

        light.attach(observer1);

        light.attach(observer2);

        

        System.out.println("Turning light ON...");

        light.turnOn();

        

        System.out.println("Turning light OFF...");

        light.turnOff();

    }

}

 

Comments

Popular posts from this blog

Archunit test

visitor design pattern