Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Observable pattern implementation in Java

I am currently Using Java's Observer/Observable Pattern and I was wondering: Why is there any need of the setChanged() method in its current implementation ? I understand that it is here so that we only have to call notifyObservers() once, at the end of our treatment.

That way, if we want to we can rollback any changes with clearChanged(). But still, we could do all the checking in our own implementation, and only call notifyObservers() when we absolutely want to update the Observers.

I'm probably missing something, but I don't understand why they didn't simplify it that way. Any ideas?

like image 761
Xalshin Avatar asked Jul 23 '15 14:07

Xalshin


1 Answers

The intent is to keep separate concerns apart. Notifying observers can be called in one place, while keeping track of changes happens in another place. Maybe for some cases you don't need this kind of separation, but the java.util.Observable implementation was designed to be able to handle complex cases, where you don't necessarily have a 1-1 correspondence between changes and notifications.

For instance you could have a requirement to throttle your notifications to avoid flooding your clients with events, which shouldn't affect tracking changes as they occur. The example below uses a worker thread that periodically calls the notification method but which doesn't know about changes, and other code that accepts changes but doesn't handle notification.

import java.util.*;

public class ObservableExample {

    public static void main(String[] args) throws Exception {
        CountTracker t = new CountTracker();
        t.addObserver(new Observer() {
            public void update(Observable observable, Object arg) {
                System.out.println("observed " + arg);
            }
        });
        t.startNotifying();
        for (int i = 0; i < 100; i++) {
            Thread.sleep(100L);
            t.incrementCount();
        }         
        t.quitNotifying();       
        System.out.println("done");         
    }
}

class CountTracker extends Observable {    
    private int count = 0;
    private Thread notificationThread;

    public synchronized void incrementCount() {
        count++;
        setChanged();
    }

    public synchronized void startNotifying() {
        if (notificationThread == null || !notificationThread.isAlive()) {
            notificationThread = new Thread(new Runnable() {
                public void run() {
                    try {
                        while (!Thread.currentThread().isInterrupted()) {
                            Thread.sleep(1000L);
                            String event = "current value of count is " 
                            + CountTracker.this.count;
                            CountTracker.this.notifyObservers(event);
                        }
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                }
            });
            notificationThread.start();
        }
    }

    public synchronized void quitNotifying() throws InterruptedException {
        if (notificationThread == null || !notificationThread.isAlive()) {
            return;
        }
        notificationThread.interrupt();
        System.out.println("wait for notification thread to terminate");
        notificationThread.join();
    } 
}

A real-life example similar to this would be implementing a progress bar, which requires translating accumulated inputs representing tasks getting done into a completion percentage and deciding how often to update the UI display.

Also consider you can always subclass java.util.Observable. The JDK library developers didn't want to have to support a huge number of subspecializations of things so they favored creating classes that were as broadly useful as possible, but if you want to eliminate some redundancy in how you use it you can create your own variation.

like image 54
Nathan Hughes Avatar answered Sep 29 '22 19:09

Nathan Hughes