7
votes

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?

1
You're asking why they didn't assume your implementation would have a certain functionality and instead provided all the tools necessary to implement things however you wanted? - Dave Newton
@dave-newton Well, said that way, the question seems stupid, sure. But, no matter how you do it, you still have to implement the checkings of whether you call clearChanged() or not. So, why not settle for an update at every notifyObservers() and not with the (seemingly superfluous) setChanged() flag ? - Xalshin
Because you might be changing things all over the place but only notifying observers once in awhile? E.g., at the end of some complex business logic that may or may not call setChanged() throughout a multi-step process: when it's done, you notifyObservers() only if it has changed during any of the business logic steps. That way the only "change tracking" you have to do is to call setChanged() and check at the end. - Dave Newton

1 Answers

5
votes

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.