The goal is to separate individual issues. Notification to observers can be triggered in one place, while change tracking takes place in another place. In most cases, you do not need this separation, but the implementation of java.util.Observable was designed to handle complex cases where you do not have to have a 1-1 correspondence between changes and notifications.
For example, you may have a requirement to ban your notifications in order to avoid flooding your customers with events that should not affect the tracking of changes as they occur. The example below uses a workflow that periodically calls the notification method, but that does not know about the changes, and other code that accepts the changes but does not process the 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 like this will implement a progress bar that requires translating accumulated inputs representing tasks completed as a percentage of completion and deciding how often to refresh the user interface display.
Also consider that you can always subclass java.util.Observable. The developers of the JDK library did not want to support a huge number of sub-implementations, so they preferred to create classes that would be as useful as possible, but if you want to eliminate some redundancy in the way you use it, you can create your own variations.
source share