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.
setChanged()throughout a multi-step process: when it's done, younotifyObservers()only if it has changed during any of the business logic steps. That way the only "change tracking" you have to do is to callsetChanged()and check at the end. - Dave Newton