Observer pattern—WeatherORama

SCENE:

1.Create an application names Weathr-ORoma that initially

provides three display elements: current conditions, weather

statistics and a simple forecast,all updated in real time as the

most recent measurements coming.

2.Weathr-ORoma wants to release an API so that other

developers can write their own weather displays.

3.Once the costomers are hooked, we intend to charge

them for each display they use.

What’s Observer pattern:

Publisher + Subscribers = Observer Pattern

or

Subject + Observers = Observer Pattern

Observer Pattern’s Characteristic:

1.One-to-many: The Observer Pattern defines a one-to-many

dependency between objects so that when one object changes

state, all of its dependents are notified and updated automatically.

2.Loose Coupling: When two objects are loosely coupled, they

can interact, but have very little knowledge of each other. The

Observer Pattern provides an object design where subjects and

observers are loosely coupled.

Design Principle:

Strive for loosely coupled designs between objects that interact.

 

Code whithout built-in java support:

The class diagram:

1. Interface Subject, Observer and DisplayElement.

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);
}
public interface DisplsyElement {
    public void display();
}

2.Implementing the Subject interface in WeatherData

public class WeatherData implements Subject {

    private ArrayList observers;
    private float temperature;
    private float humidity;
    private float pressure;

    public WeatherData() {
        observers = new ArrayList();
    }

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

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

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

    public void measurementsChanged() {
        notifyObservers();
    }

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

3.Build those display elements, here we build just one of them.

public class CurrentConditionsDisplay implements Observer, DisplsyElement {

    private float temp;
    private float humidity;
    private float pressure;
    private Subject weatherData;

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

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

    @Override
    public void display() {
        System.out.println("Cuttent condictions: " + temp + " F degrees and "
                + humidity + "% humidity and " + pressure + "Pascal.");
    }
}

4.Create a test harness

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

        CurrentConditionsDisplay currentDisplay = 
            new CurrentConditionsDisplay(weatherData);

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

5.Output

Cuttent condictions: 80.0 F degrees and 65.0% humidity and 30.4 Pascal.
Cuttent condictions: 82.0 F degrees and 70.0% humidity and 29.2 Pascal.
Cuttent condictions: 78.0 F degrees and 64.0% humidity and 31.8 Pascal.

 

Code whith built-in java support:

The most general is the Observer interface and the Observable class in

the java.util package. These are quite similar to our Subject and Observer

interface, but give you a lot of functionality out of the box. You can also

implement either a push or pull style of update to your observers.

The class diagram:

How Java’s built-in Observer Pattern works:

The most obvious difference is that WeatherData (our subject)

now extends the Observable class and inherits the add, delete

and notify Observer methods(among a few others).

For an Object to become an observer…

1.implement the Observer(java.util.Observer) interface.

2.call addObserver() on any Observable object.

3.to removeyourself as an observer just call deleteObserver().

For the Observable to send notifications…

1.be Observable by extending the java.util.Observable superclass.

2.must call the setChanged() method to signify that the state

has changed in your object.

3.call one of two notifyObservers() methods: either notifyObservers()

or notifyObservers(Object arg).

* **notifyObservers()—-the Observer has to “pull” the data it wants from

the Observable object passed to it.

***notifyObservers(Object arg)—-“push” data to the observers.

For an Observer to receive notifications…

Just implements the update method: update(Observable o, Object arg)

Rework WeatherData to use java.util.Observable:

import java.util.Observable;

public class WeatherData extends Observable {

    private float temperature;
    private float humidity;
    private float pressure;

    public WeatherData() {
    }

    public void measurementsChanged() {
        setChanged();
        notifyObservers();
    }

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

    public float getHumidity() {
        return humidity;
    }

    public float getTemperature() {
        return temperature;
    }

    public float getPressure() {
        return pressure;
    }
}
import java.util.Observable;
import java.util.Observer;

public class CurrentConditionsDisplay implements Observer, DisplsyElement {

    Observable observable;
    private float temp;
    private float humidity;
    private float pressure;

    public CurrentConditionsDisplay(Observable observable) {
        this.observable = observable;
        observable.addObserver(this);
    }

    /**
     * This method is called whenever the observed object is changed. An
     * application calls an <tt>Observable</tt> object's
     * <code>notifyObservers</code> method to have all the object's
     * observers notified of the change.
     *
     * @param o   the observable object.
     * @param arg an argument passed to the <code>notifyObservers</code>
     */
    @Override
    public void update(Observable o, Object arg) {
        if (o instanceof WeatherData) {
            WeatherData weatherData = (WeatherData) o;
            this.temp = weatherData.getTemperature();
            this.humidity = weatherData.getHumidity();
            this.pressure = weatherData.getPressure();
            display();
        }
    }

    @Override
    public void display() {
        System.out.println("Cuttent condictions: " + temp + " F degrees and "
                + humidity + "% humidity and " + pressure + " Pascal.");
    }
}

Same test class, same output.

Ok, we’ve got all about Observer Pattern. The end of this article……

A little life-changing application—-I’ll call it ForGozSake!

public class SwingObserverExample {
    JFrame frame;

    public static void main(String[] args) {
        SwingObserverExample example = new SwingObserverExample();
        example.go();
    }

    public void go() {
        frame = new JFrame("Bazinga!");
        JButton button = new JButton("Should I do it?");
        int yesOrNo = (int) (Math.random() * 2);
        if (0 == yesOrNo) {
            button.addActionListener(new AngelListener());
        } else if (1 == yesOrNo) {
            button.addActionListener(new DevilListener());
        }
        frame.getContentPane().add(BorderLayout.CENTER, button);
        frame.setSize(300, 400);
        frame.setBackground(Color.pink);
        frame.setLocation(1000, 600);
        frame.setVisible(true);
    }

    class AngelListener implements ActionListener {

        /**
         * Invoked when an action occurs.
         *
         * @param e
         */
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("Don't do it, you might regret it");
        }
    }

    class DevilListener implements ActionListener {

        /**
         * Invoked when an action occurs.
         *
         * @param e
         */
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("Come on, do it!");
        }
    }
}

Yes or No?

Leave a Reply

Your email address will not be published. Required fields are marked *