13

At the moment I'm studying design patterns and I've come to a part where I'm confused whether the observer pattern makes use of the push mechanism or does it make use of the pull mechanism?

I've read different implementations of this and can't really establish which one is correct.

Also, I'd like to know three straight forward advantages of the push model towards the pull model. I guess one of them is that the push model is less coupled then the pull model?

Visores
  • 4,008
  • 4
  • 18
  • 29
Liusara
  • 141
  • 1
  • 1
  • 7
  • This is answered in detail here: https://softwareengineering.stackexchange.com/questions/253398/the-observer-pattern-using-the-pulling-mechanism – lbalazscs Apr 17 '17 at 21:45
  • Both the push and pull models are valid implementations of the Observer pattern, according to the [GoF book](https://stackoverflow.com/a/63326502/1371329). – jaco0646 Jan 06 '23 at 14:41

4 Answers4

40

Observer Pattern in detail (with the focus on questions asked)


  • Definition : The Observer Pattern defines a one-to-many dependency between the objects so that when one object changes state, all of its dependents are notified and updated automatically

  • 3 things to focus:

    1. Observable object - The object being observed.
    2. Observer objects - The objects that observe the observable object
    3. Communication Mechanism - Pull or Push Mechanism

At the moment i'm studying design patterns and i've came to a part where i'm confused whether the observer pattern makes use of the push mechanism or does it makes use of the pull mechanism ?

Confusion might be because you are literary going by name - Pull or Push.

Please note that in both mechanisms, it is always the responsibility of Observable object to notify all the subscribed observers, but the difference lies whether [Push->]the observer get the exact data it wants or [Pull->] it get the data wrapped in some object (mostly Observable object) & it has to extract the required data from it.

  • Push -> Observer get the required data directly
  • Pull -> Observer get the data wrapped in an object and it needs to extract that.

I've read different implementations of this and can't really establish which one is correct.

It is not the question of correction here, actually both will work absolutely fine in any situation. It's just which is best suited to a particular scenario/situation which can be easily analysed if we see following details for both the mechanism.

Also i'd like to know three straight forward advantages of the push model towards the pull model. I guess one of them is that the push model is less coupled then the pull model ?

I may not be able to provide 3 advantages, but let me try if I can give you a clear picture of where to use what by using use-case examples:

  • Push Mechanism

    • This is purely the Observable's responsibility, Observer just need to make sure they have put required code in their update methods.
    • Advantages
      • The main advantage of the 'push' model is lower coupling between the observer and the subject.
        • Observable & Observer both are interfaces/abstract classes which is actually a design principle - Program to interface or supertypes
    • Disadvantage
      • Less flexibility : As Observable needs to send the required data to the Observer and it would become messy if we have say 1000 observers and most of them require different types of data.
    • Use-Case
      • It should be used when there are max 2-3 different types of Observers (different types means observer require different data) or all observers require same type of data.
      • Like token systems in a bank
        • In this all observers (different LEDs) just need one notification the list of updated waiting token numbers, so may better be implemented in this way as compared to Pull Mechanism.
  • Pull Mechanism

    • In pull mechanism as well, it is the Observable's responsibility to notify all observer that something has been changed, but this time Observable shares the whole object which has the changes, some observers might not require the complete object, so Observers just need extract the required details from that complete project in their update methods.
    • Advantages
      • The advantage of this is more flexibility.
        • Each observer can decide for itself what to query, without relying on the subject to send the correct (only required) information.
    • Disadvantage
      • The observers would have to know things about the subject in order to query the right information from the shared complete object.
    • Use-Case
      • It should be used when there are more than 2-3 different types of Observers (different types means observer require different data)
      • Like publishing of FX Rates by any foreign exchange rates provider for different investment banks
        • In this Some banks just deals with only INR, some others only GBP etc. so it should be implemented using Pull mechanism as compared to Push mechanism.

References

Thomas
  • 43
  • 6
amandeep1991
  • 1,344
  • 13
  • 17
1

This is an example code that uses the "PULL" mode as explained above Observers could get different types of data (not implemented in this case).

import java.io.*;
import java.util.*;

 interface Observer{
    public void update();
}

 interface Observable {
    public void notifyAll() throws Exception;
    public void notify(Observer o) throws Exception;
}
class Suscriber implements Observer {
    String id;
    Subject subject;
    boolean registered = false;
    Double data = 0.0;
    public Suscriber(String id,Subject sub){
        this.id = id;
        subject = sub;    

        subject.register(this);
        registered = true;
    }

    public void update() {
        if(registered){
            data = subject.getData();
        }
        display();
    }

    void display(){
        System.out.println("Suscriber:" + id + " updated");
        System.out.println("Current DATA: " + data);
    }
}
class Subject implements Observable{
    private List<Observer> observers = new ArrayList<Observer>();
    private Double data = 0.0;

    public void register(Observer o){
        observers.add(o);
    }
    public void unregister(Observer o){
        int i =    observers.indexOf(o);

        observers.remove(i);
    }

    public void notify(Observer o) throws Exception{
          o.update();
    }

    public void notifyAll() throws Exception {
        for(Observer o:observers)
          this.notify(o);
    }

    public void computeMetrics(){
        try{
        long bunch = System.currentTimeMillis()/2;
        data = data + new Double(bunch);
        this.notifyAll();
        }catch(Exception ex){
            ex.printStackTrace();
        }
    }

    public Double getData() {
        return this.data;
    }
}

class ObserverTestDrive {
    public static void main (String[] args) {

        Subject subject = new Subject();

        long transmission = 10000;

        Suscriber norths = new Suscriber("NorthStation",subject);
        Suscriber wests = new Suscriber("WestStation",subject);
        Suscriber souths = new Suscriber("SouthStation",subject);

        for(int i=0;i<transmission;i++)
            subject.computeMetrics();










    }
}
davidza5
  • 86
  • 1
  • 10
0

Here are two examples (one using push and other using pull) from Head First Design Patterns.

Push:

public interface Subject {
    public void registerObserver(Observer o);
    public void removeObserver(Observer o);
    public void notifyObservers();
}

public interface Observer {
    public void update(float temp, float humidity, float pressure);
}

import java.util.*;

public class WeatherData implements Subject {
    private ArrayList<Observer> observers;
    private float temperature;
    private float humidity;
    private float pressure;

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

    public void registerObserver(Observer o) {
        observers.add(o);
    }

    public void removeObserver(Observer o) {
        int i = observers.indexOf(o);
        if (i >= 0) {
            observers.remove(i);
        }
    }

    public void notifyObservers() {
        for (int i = 0; i < observers.size(); i++) {
            Observer observer = (Observer)observers.get(i);
            observer.update(temperature, humidity, pressure);
        }
    }

    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        notifyObservers();
    }

    public float getTemperature() {
        return temperature;
    }

    public float getHumidity() {
        return humidity;
    }

    public float getPressure() {
        return pressure;
    }

}

public class CurrentConditionsDisplay implements Observer {
    private float temperature;
    private float humidity;
    private WeatherData weatherData;

    public CurrentConditionsDisplay(WeatherData weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }

    public void update(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        display();
    }

    public void display() {
        System.out.println("Current conditions: " + temperature
            + "F degrees and " + humidity + "% humidity");
    }
}

public class StatisticsDisplay implements Observer {
    private float maxTemp = 0.0f;
    private float minTemp = 200;
    private float tempSum= 0.0f;
    private int numReadings;
    private WeatherData weatherData;

    public StatisticsDisplay(WeatherData weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }

    public void update(float temp, float humidity, float pressure) {
        tempSum += temp;
        numReadings++;

        if (temp > maxTemp) {
            maxTemp = temp;
        }

        if (temp < minTemp) {
            minTemp = temp;
        }

        display();
    }

    public void display() {
        System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings)
            + "/" + maxTemp + "/" + minTemp);
    }
}

public class ForecastDisplay implements Observer, DisplayElement {
    private float currentPressure = 29.92f;
    private float lastPressure;
    private WeatherData weatherData;

    public ForecastDisplay(WeatherData weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }

    public void update(float temp, float humidity, float pressure) {
        lastPressure = currentPressure;
        currentPressure = pressure;

        display();
    }

    public void display() {
        System.out.print("Forecast: ");
        if (currentPressure > lastPressure) {
            System.out.println("Improving weather on the way!");
        } else if (currentPressure == lastPressure) {
            System.out.println("More of the same");
        } else if (currentPressure < lastPressure) {
            System.out.println("Watch out for cooler, rainy weather");
        }
    }
}

public class WeatherStation {

    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();

        CurrentConditionsDisplay currentDisplay =
            new CurrentConditionsDisplay(weatherData);
        StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
        ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);

        weatherData.setMeasurements(80, 65, 30.4f);
        weatherData.setMeasurements(82, 70, 29.2f);
        weatherData.setMeasurements(78, 90, 29.2f);
    }
}

Pull:

public interface Subject {
    public void registerObserver(Observer o);
    public void removeObserver(Observer o);
    public void notifyObservers();
}

public interface Observer {
    public void update();
}

import java.util.*;

public class WeatherData implements Subject {
    private ArrayList<Observer> observers;
    private float temperature;
    private float humidity;
    private float pressure;

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

    public void registerObserver(Observer o) {
        observers.add(o);
    }

    public void removeObserver(Observer o) {
        int i = observers.indexOf(o);
        if (i >= 0) {
            observers.remove(i);
        }
    }

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

    public void setMeasurements(float temperature, float humidity, float pressure) {
        this.temperature = temperature;
        this.humidity = humidity;
        this.pressure = pressure;
        notifyObservers();
    }

    public float getTemperature() {
        return temperature;
    }

    public float getHumidity() {
        return humidity;
    }

    public float getPressure() {
        return pressure;
    }

}

public class CurrentConditionsDisplay implements Observer {
    private float temperature;
    private float humidity;
    private WeatherData weatherData;

    public CurrentConditionsDisplay(WeatherData weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }

    public void update() {
        this.temperature = weatherData.getTemperature();
        this.humidity = weatherData.getHumidity();
        System.out.println("Current conditions: " + temperature
                + "F degrees and " + humidity + "% humidity");
    }
}

public class StatisticsDisplay implements Observer {
    private float maxTemp = 0.0f;
    private float minTemp = 200;
    private float tempSum= 0.0f;
    private int numReadings;
    private WeatherData weatherData;

    public StatisticsDisplay(WeatherData weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }

    public void update() {
        float temp = weatherData.getTemperature();
        tempSum += temp;
        numReadings++;

        if (temp > maxTemp) {
            maxTemp = temp;
        }

        if (temp < minTemp) {
            minTemp = temp;
        }

        System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings)
                + "/" + maxTemp + "/" + minTemp);
    }
}

public class ForecastDisplay implements Observer {
    private float currentPressure = 29.92f;
    private float lastPressure;
    private WeatherData weatherData;

    public ForecastDisplay(WeatherData weatherData) {
        this.weatherData = weatherData;
        weatherData.registerObserver(this);
    }

    public void update() {
        lastPressure = currentPressure;
        currentPressure = weatherData.getPressure();

        System.out.print("Forecast: ");
        if (currentPressure > lastPressure) {
            System.out.println("Improving weather on the way!");
        } else if (currentPressure == lastPressure) {
            System.out.println("More of the same");
        } else if (currentPressure < lastPressure) {
            System.out.println("Watch out for cooler, rainy weather");
        }
    }
}

public class WeatherStation {

    public static void main(String[] args) {
        WeatherData weatherData = new WeatherData();

        CurrentConditionsDisplay currentDisplay =
            new CurrentConditionsDisplay(weatherData);
        StatisticsDisplay statisticsDisplay = new StatisticsDisplay(weatherData);
        ForecastDisplay forecastDisplay = new ForecastDisplay(weatherData);

        weatherData.setMeasurements(80, 65, 30.4f);
        weatherData.setMeasurements(82, 70, 29.2f);
        weatherData.setMeasurements(78, 90, 29.2f);
    }
}

The difference being, in case of Push, the Subject not only let the observer know that he has new data, he also send the data to Observer (regardless of whether the Observer asked for it or not).

For example, let's check the update method of StatisticsDisplay in push:

public void update(float temp, float humidity, float pressure) {
    tempSum += temp;
    numReadings++;

    if (temp > maxTemp) {
        maxTemp = temp;
    }

    if (temp < minTemp) {
        minTemp = temp;
    }

    display();
}

In case of Pull, the Subject not only let the observer know that he has new data. The Observer use the getter methods on the Subject to pull the values it needs.

For example, let's check the update method of StatisticsDisplay in pull:

public void update() {
    float temp = weatherData.getTemperature();
    tempSum += temp;
    numReadings++;

    if (temp > maxTemp) {
        maxTemp = temp;
    }

    if (temp < minTemp) {
        minTemp = temp;
    }

    System.out.println("Avg/Max/Min temperature = " + (tempSum / numReadings)
            + "/" + maxTemp + "/" + minTemp);
}

You might have also noticed that in case of pull, update method does not have any arguments.

Ahmad Ismail
  • 11,636
  • 6
  • 52
  • 87
-2

The observer pattern uses push since the observable object push notifications to its subscribers.

Push vs Pull (in web mostly): Push - the server sends(push) notifications to clients, this means it needs keep tracking on their address (URI) or in the more general case their reference.

Pull - the client is responsible for requesting fresh data from the server.

The pattern is not only for Web and is used all over, for example in desktop applications.

user440850
  • 101
  • 4
  • So u can't implement 'pull' in the observer pattern? Quote of a programmerwebsite : When implementing the Observer pattern, there are two main approaches to consider: the 'push' model and the 'pull' model. – Liusara Jan 10 '16 at 14:05
  • 2
    What the pull / push means (in your question) is how the client receive the updated data. In the push version the observable push / sends the data to the client. in Pull the client gets the notification from the observable (again this is push) and pull the data on its own. so in the pull model it gets notified that there was a change and than decide what to do. – user440850 Jan 10 '16 at 14:36
  • rxjs Observable is implemented as push. but observable pattern can be push or pull. it depends on the implementation – HungNM2 Aug 30 '20 at 07:45