Java Design:
Building Better Apps and Applets

Chapter 5: Design with Notification

This document is a mirror page!

This file is a ROUGH conversion of a JAVA DESIGN pre-production file into HTML, using HoTmeTaL Pro.

JAVA DESIGN includes over 170 illustrations. None are included here (HoTMeTaL Pro does not automatically convert .wmf artwork).

So these pages will give you a good idea about what the book is all about, yet is no substitute for the real thing (thank goodness!).

Title: JAVA DESIGN: Building Better Apps and Applets
Authors: Peter Coad and Mark Mayfield
ISBN: 0-13-271149-4

Back to Table of Contents

Design with Notification

In this chapter, we'll explore three major notification mechanisms:

Passive notification is simple yet resource-intensive.

Timer-based notification is a useful pattern.

Yet active notification is most interesting; it's an essential ingredient for problem-domain object reuse; it's an essential ingredient for designing loosely-coupled subsystems. Java's own active notification mechanism (observable-observer) is defective; this chapter goes beyond that weakness, showing you how to really get the job done.

Passive Notification

Passive notification is the simplest of all notification mechanisms.

Passive notification is also known as polling. One object polls others asking each one for its current status, to see if it has changed in some meaningful way.

Perhaps you have a boss like this-always checking on you, always wondering how you are doing. In three words: a real pain:

Figure 5-. Passive notification

An object model for passive notification looks like this:

Figure 5-. An object model for passive notification

The corresponding object interactions are just what you'd expect:

Figure 5-. A scenario view for passive notification

Okay, let's apply this notification mechanism to Zoe's Zones:

Figure 5-. Passive notification for Zoe's Zones-object model

Figure 5-. Passive notification for Zoe's Zones-scenario view

The good news about passive notification is that it is simple.

The bad news is that passive notification is resource intensive in two ways: the loop itself, and the number of objects that must be queried. Passive notification consumes resources asking about status, even if the status never changes.

You can take care of the resource-intensive aspect of the loop itself by adding a timer (something we'll explore later in this chapter). However, the time-to-respond suffers. And if a problem occurs while you are sleeping, it might go undetected (depending upon the problem domain you are working on).

When you have hundreds or thousand of objects to watch over (for example, the hundreds of items that might appear in a list within a UI), passive notification is still too awkward, too slow.

Active notification to the rescue!

Timer-Based Notification

Timer-based notification is another notification mechanism.

The idea behind a timer is simple: put a thread to sleep for a specified period of time, then let it wake up and continue.

Timer-notification pattern

The timer will sleep until a specified duration has passed. Here's the object model:

Figure 5-. Timer object model

Throughout this chapter, we'll need this strategy:

"Holder-Interface" Strategy: Establish a collection; define an interface.

Here's how that strategy fits in with the timer object model:

Figure 5-. Holder-interface, again and again

Next up: the timer scenario view-with a builder object, to set up and activate a timer:

Figure 5-. Timer scenario view

In Java, it looks like this:

public interface ITime {

void wakeup(); }

public interface ITimer {

void activate(long duration);

void deactivate(); }

public class Timer extends Object implements Runnable, ITimer {

#

// attributes / private

private long duration = 0;// in milliseconds

// attributes / private / object connection

private ITime myITime;

Thread myThread;

// methods / public / Runnable implementation

public void run() {

for (;;) { // continue sleep/wake-up cycle until thread is stopped

try {

Thread.currentThread().sleep(this.duration);

} catch (InterruptedExecution e) {};

this.myITime.wakeup(); } }

Code notes: Sleep is a class method. So we send a message to the Thread class itself, asking it to put this thread to sleep.

// methods / public / ITimer implementation

public void activate(int aDuration) {

// remember the duration

this.duration = aDuration;

// create and start a thread

myThread = new Thread(this);

myThread.start(); }

public void deactivate() {

myThread.stop(); }

#

}

Code notes: Make sure to catch the exception that's thrown when putting a thread to sleep, or the compiler will complain.

public class ITimeImplementer extends Object implements ITime {

#

// attributes / private

private ITimer myITimer;

// methods / public / conducting business

public void setup() {

this.myITimer = new Timer(); }

public void activateTimer() {

this.myITimer.activate(3600000); // 1 hr = 3600000 milliseconds }

public void deactivateTimer() {

this.myITimer.deactivate(); }

// methods / public / ITime implementation

public void wakeUp() {

// code goes here }

#

}

Code notes: We put the creation of the timer in the setup method and the activation of the timer in the activateTimer method. Alternatively, we could put both of these steps in the constructor of ITimeImplementer if we want to create and start a timer right away.

A timer for Charlie's Charters

Suppose that once per day, we need to generate a list of expiring reservations for each agent.

This requires timer(s) and low-priority thread(s).

We can design this in several ways:

The "one thread per agent" approach has more low-priority threads. Yet it also points to a simpler overall design. Let's take a closer look at it.

The object model looks like this:

Figure 5-. A timer for Charlie's Charters-object model

And the scenario view looks like this:

Figure 5-. A timer for Charlie's Charters-scenario view

In Java, it looks like this:

public class Agent extends Object implements ITime {

#

// private / attributes

private ITimer myITimer;

// methods / public / conducting business

public void setup() {

this.myITimer = new Timer(); }

public void activateTimer() {

this.myITimer.activate(86400000); // 24 hr = 86400000 milliseconds }

public void deactivateTimer() {

this.myITimer.deactivate(); }

// methods / public / ITime implementation

public void wakeUp() {

// generate expiration list

// find expirations

// notify expiration UI }

#

}

Code notes: We set the timer to wake us up every 24 hours. When the agent is told to wake up, it generates a list of expiring reservations and notifies the appropriate UI component.

Active Notification

Active notification puts the notification responsibility within the object that changes. That object takes action.

Hmmm. Sounds much more like an object-oriented approach: I change; I let others (who have registered interest in me) know that I've changed.

We can summarize active notification in two words: observable-observer.

Let's take a closer look at observable-observer-and significant variations on that theme:

Observable-observer

Observable-observer is an object-model pattern (also known as publisher-subscriber, model-view, and document-view).

Figure 5-. An observable and its observers

Here's how it works.

First, someone tells an observable who its observers are:

Then, an observable notifies its observers:

- When an observable object changes itself in a significant way (meaning, in a way that someone else might be interested in), it notifies each of its observers.

Finally, each observer does its thing:

Okay, okay: we hear you out there. Lots of messages? Actually, compared with passive notification:

Let's consider the point about reduced coupling.

Observable-observer lets us put together PD and UI objects in a way that PD objects know very, very little about UI objects. PD objects are not hopelessly attached to UI objects-a very good thing. Observable-observer facilitates reuse of PD objects.

In a similar vein, observable-observer lets us put together a subsystem with other, supporting subsystems. A subsystem knows very, very little about the supporting subsystems. That subsystem no longer has a fatal attraction to its supporting subsystems. Observable-observer facilitates reuse of a subsystem (loose coupling); at the same time, facilitate extensibility as well (easy to add, change, or remove supporting subsystems, as needed).

Okay, then. So how might we model (and ultimately implement) something called observable-observer? Classes and inheritance? Composition and interfaces? Or?

Several options are possible.

"Balanced Design" Strategy: Design at one extreme, another extreme, and then somewhere in between. Design connotes looking at alternative and picking a reasonable approach.

Let's consider the extremes-and then the middle ground.

A pair of classes (one extreme)

One extreme is a pair of classes-without inheritance, or with inheritance.

Without inheritance: we'd need to design and build both observable and observer into the classes that needed them, each and every time, from scratch.

Figure 5-. A pair of classes

Sounds like too much work. We can do better than that.

Some methods remain the same:

addObserver-adds an object to a list of observers

deleteObserver-deletes an object from a list of observers

deleteObservers-deletes all observers from a list of observers

notifyObservers-send an "update" message to each observer,

letting it know about a change that has taken place.

Yet some methods must be customized for a specific observable and its observers:

Observable

methodResultingInStateChg-do something significant,

something worth notifying the observers about

getStatus-get the value(s) requested by an observer, something it needs

to ask, upon notification that what it is watching (the observable)

has changed in some way.

Observer

update-initiate an observer's response to a notification from an observable

actUponIt-upon notification, this is the action that that each observer

takes

Some methods remain the same, some methods must be customized.

This sounds like a good opportunity for inheritance, showing what is the same (superclass) and what is different (subclasses).

Is this a good idea (remember the chapter on "Design with Inheritance, Rather Than Composition)? Let's evaluate this, in the pages ahead. For now, though, we're in the midst of considering an extreme: a classes-only approach.

Let's try it out. Here's an object model:

Figure 5-. A pair of specialized classes

A SpecializedObservable class needs its own method name for methodResultingInStateChg and getStatus. It also needs its own implementation for those methods.

A SpecializedObserver class needs its own method name for actUponIt; and it needs its own implementation for it.

Yet what about the update method? It's listed in both Observer and SpecializedObserver. In an object model, seeing a method name appear in a class and a subclass indicates:

What about in this case? The Observer class has an update method. There is no implementation behind it; every "update" method is something we must work out for each subclass of Observer. No implementation? That sounds exactly like what an interface is all about: an interface is a list of one or more method signatures-no implementation. Just method signatures, no implementation: that's a good hint that we should be using an interface here.

"Extracting Interfaces from a Class Hierarchy" Strategy: When you find that a subclass is inheriting one or more methods that are merely method signatures, use an interface for those method signatures (that's exactly what an interface is for).

Let's also take a look at the corresponding scenario view:

Figure 5-. Interactions for objects in a pair of specialized classes

Consider the parameters for the update method.

The first parameter tells the observer object whom it needs to be talking to-and obviates the need for an observer to keep a list of its observables. So including the first parameter simplifies an observer: one less collection to maintain.

The second parameter tells the observer what kind of change has occurred-and obviates the need for an observer to ask for every status-related attribute in each observable, hoping to find out what has indeed changed. So including the second parameter simplifies an observer's job, as well as reduces the number of object interactions between an observer and each of its observables.

A pair of interfaces (another extreme)

Let's continue to be extremists, at least for a while longer.

Interfaces are cool. How about a solution made up entirely of interfaces?

Figure 5-. An interfaces-only object model

Yet wait a minute. We can't draw something like that-it makes a promise that we cannot keep. You see, an interface cannot be required to hold a collection of objects.

An interface is simply a collection of method signatures-no more, no less.

Can an interface imply that an implementer might hold some number of other objects?

Yes, with some agreed-upon naming conventions, we could imply attributes (get, set) and object connections (add, remove). Still, no, we cannot require an implementer of that interface to build it that way. Remember, an interface is a collection of method signatures-that's all.

Here, we need more than just interfaces; interfaces are not enough, when it comes to building effective object models. We also need classes that implement those interfaces.

Classes and an interface (standing on shaky ground)

Let's go for the middle ground: inheritance for observables, interfaces for observers.

In fact: Java includes an Observable class and an Observer interface.

In other words: Observable has implementation and interface we can inherit; Observer defines an interface we can implement.

The middle ground looks something like this:

Figure 5-. Middle ground

From the model, we see that a specialized observable object knows some number of observers (objects in classes that implement the Observer interface, that is). That makes good sense.

Java's Observable class?

Is it really such a good idea to inherit from Observable? Is this really the way to go on this?

Let's check it out.

Each time we have a new kind of observable, we must add a new specialized observable class and new classes that implement the corresponding observer interfaces.

For example, suppose we add a specialization of Observable, called Person. If PersonObservable includes name and address, then we would add:

- A specialized observable class (Person)

with getName and setName methods

We could reuse the interfaces, as long as the parameter lists for the method signatures are not hardwired back to objects in a specific class (such as PersonObservable, for example).

Yet is Observable, specializing into Person, really a valid use of inheritance? Let's apply the strategy from the "Design with Composition (Rather than Inheritance)" chapter:

"When to Inherit" Strategy: Inheritance to extend attributes and methods (yet encapsulation is weak within a class hierarchy, so use this mechanism in limited ways). Use it when you can satisfy these criteria:

(1) "Is a special kind of," not "is a role played by a"

(2) Never needs to transmute to be an object in some other class

(3) Extends, rather than overrides or nullifies

(4) Does not subclass what is merely a utility class

(5) Within PD: Expresses special kinds of roles, transactions, or devices.

Person, if made a subclass of Observable, would subclass what is merely a utility class. Not a good idea.

Hence, we really ought to use composition here, rather than inheritance. Why? Composition is easier to change, easier to add to existing classes, easier when it comes to providing several flavors of that functionality (should the need arise for that).

Should we inherit from Java's Observable class? The answer is no. Java's Observable class is not very useful. An ObservableComponent class would be far better (that's something we'll consider further, in the pages ahead).

Java's Observer interface?

Java has an Observer interface. It looks like this:

public interface Observer {

void update(Observable observed, Object argument); }

At this point, what we'd like to do is:

(1) build our own ObservableComponent class, so we can use observable components whenever we want to, using composition (rather than inheritance), and

(2) use a corresponding Observer interface.

Can we use Java's Observer interface? Or do we need to define one of our one?

Let's take a closer look at that interface declaration, specifically at the parameters:

Observable observed

By specifying that the interface works with objects in the class Observable or its subclasses, rather than allowing for objects from Object or its subclasses:

Object observed

the designer of this Java interface severely limited its usefulness.

Java's Observer interface assumes that it only needs to work with objects that are in the Observable class or its subclasses. That's not a good news: we've already seen that inheriting from Observable is not the way to go.

In fact, we cannot use Java's Observer interface in our design. Why? That interface assumes that it works only with objects in the class Observable or its subclasses. Yet we need interface that will work with objects from our own ObservableComponent class.

Can we use Java's Observer interface? No. In fact, its design weakness inspired this strategy:

"Don't Limit Your Interfaces with Needless Assumptions" Strategy: Type your interface parameters as a built-in type (for example: int, float, String, StringBuffer, Object). Let each implementer of that interface test for the specific classes of objects it works with. Reason: increase the likelihood of reuse of each interface.

How do we overcome this problem? We simply introduce a very similar, yet more general interface, called IObserver:

public interface IObserver {

void update(Object observed, Object argument); }

Composition and interfaces (to the rescue)

Composition and interfaces-an awesome combination!

Actually, we used this "composition and interface" dynamic duo earlier in this book, in the "Design with Interfaces" chapter, with:

We add-in capability by building the composition and implementing the promised interface. The composition object ends up with some "added-in" capabilities.

Footnote: The same design approach may be applied when building C++ apps, too. You can dedicate part of your class hierarchies to expressing interfaces, namely: C++ classes that consist only of method signatures. On the other hand, when designing C++ apps, you can also "mix-in" behaviors from multiple superclasses; however, the ever-decreasing amount of encapsulation within a class hierarchy with multiple-inheritance behavior makes this an unwieldy approach (something to avoid, when possible).

It's now time to apply both ObservableComponent and IObserver.

Here's an informal sketch:

Figure 5-. Working with an observable component

The object model looks like this:

Figure 5-. Using an observable component

A scenario view is next. Note that in the scenario view:

Here it is:

Figure 5-. Interactions with an observable component

Here is what happens:

- When an observable has a state change, it send a message to its own

observable component object.

Then each observer:

In Java, it looks like this:

public interface IObserver {

void update(Object theObserved, Object changeCode); }

public interface IObservable {

addIObserver(IObserver anIObserver);

deleteIObserver(IObserver anIObserver);

deleteIObservers(); }

public class ObservableComponent extends Object {

#

// attributes / private / object connections

private Vector myIObservers = new Vector();

// methods / public / accessors for object connection values

public void addIObserver(IObserver anIObserver) {

this.myIObservers.addElement(anIObserver); }

public void deleteIObserver(IObserver anIObserver) {

this.myIObservers.removeElement(anIObserver); }

public void deleteIObservers() {

this.myIObservers.removeAllElements(); }

// methods / public / notification

public void notifyIObservers(Object theObserved, Object changeCode) {

// iterate through the vector of IObservers and tell each IObserver to update

Enumeration myIObserversList = this.myIObservers.elements();

while (myIObserversList.hasMoreElements()) {

// must cast the element to IObserver

IObserver anIObserver = (IObserver) myIObserversList.nextElement();

anIObserver.update(theObserved, changeCode); } }

#

}

public class IObserverImplementer extends Object implements IObserver {

#

// methods / public / IObserver implementation

public void update(Object theObserved, Object changeCode) {

if (changeCode instanceof String) {

String theChangeCode = (String)changeCode;

// if theChangeCode is the one I'm looking for,

// then get the status from theObserved } }

#

}

public class IObservableImplementer extends Object implements IObservable {

#

// attributes / private

private int state; // something that represents my state

// attributes / private / object connections

private ObservableComponent myObservableComponent = new ObservableComponent();

// methods / public / IObservable implementation

public void addIObserver(IObserver anIObserver) {

this.myObservableComponent.addIObserver(anIObserver); }

public void deleteIObserver(IObserver anIObserver) {

this.myObservableComponent.deleteObserver(anIObserver); }

public void deleteIObservers() {

this.myObservableComponent.deleteIObservers(); }

// methods / public / resulting in a state change

public void methodResultingInStateChange(int newState) {

this.state = newState;

// instruct my observable component to notify the IObservers

// pass myself as the observed and "state" as the change code

this.myObservableComponent.notifyIObservers(this, "state"); }

#

}

Code notes: The Builder in the previous scenario can be just about any object that builds the relationships between an IObservableImplementer object and its IObserverImplementer objects.

PD-to-UI notification for Charlie's Charters

Let's apply this "observable component-iObserver interface" pattern to Charlie's Charters and its flight descriptions.

When should we message, when should we notify?

"Message Inward, Notify Outward" Strategy:

UI-invoked changes: message inward, from UI to PD.

PD-invoked changes: notify outward, from PD to UI.

Consider what happens when the state of a flight description changes. For example, if the departureTime changes, any UI observers of that flight description should be notified (so the UI observers can update themselves accordingly).

Applying the "observable component-iObserver interface" pattern, we get this:

Figure 5-. A flight description and its observable component

The flight-description class is application-specific. The DateReserveUI is something that can be reused in analogous (in this case, date reserving) application. And the ObservableComponent class is reused as-is (it stays the same, for any application).

A scenario view? It needs a builder object to set things up. It needs a sender object, representing some PD object or a UI object that sends a setFrom message. Then it's ready to roll, like this:

Figure 5-. Interactions for a flight description and its observable component

In Java, it looks like this:

public interface ITimeDuration {

Date getDepartureTime();

void setDepartureTime(Date aTime);

Date getArrivalTime();

void setArrivalTime(Date aTime); }

Code notes: In Java, both dates and times are handled in the Date class.

public class FlightDescription extends Object implements ITimeDuration {

#

// attributes / private

private Date departureTime;

private Date arrivalTime;

// attributes / private / object connections

private ObservableComponent myObservableComponent = new ObservableComponent();

// methods / public / ITimeDuration implementation

public Date getDepartureTime() {

return this.departureTime; }

public void setDepartureTime(Date aTime) {

this.departureTime = aTime;

// tell my observable component to notify the IObservers

this.myObservableComponent.notifyIObservers(this, "departureTime");

public Date getArrivalTime() {

return this.arrivalTime; }

public void setArrivalTime(Date aTime) {

this.arrivalTime = aTime;

// tell my observable component to notify the IObservers

this.myObservableComponent.notifyIObservers(this, "arrivalTime");

#

}

public class DateReserveUI implements IObserver {

#

// methods / public / IObserver implementation

public void update(Object theObserved, Object changeCode) {

// make sure the change code is a string

if (changeCode instanceof String) {

String theChangeCode = (String)changeCode;

// check to see if the correct change code

if (theChangeCode.equalsIgnoreCase("departureTime")) {

// make sure the observed is an ITimeDuration

if (theObserved instanceof ITimeDuration) {

ITimeDuration anITimeDuration = (ITimeDuration)theObserved;

// get the new departure time from the iTimeDuration

Date newDepartureTime = anITimeDuration.getDepartureTime();

// update the UI with the new departure time } } } }

#

}

Code notes: We use three nested if statements in the update method. It can be written as three separate if statements that immediately return from the method is a condition is not met. We make these type checks just to be on the safe side.

ObservableComponent is the same code as the previous example.

So, just how important is the "observable component-iObserver interface?

"Observable component-iObserver interface" is the notification mechanism for keeping UI objects in-sync with corresponding PD objects.

Without it, we'd be stuck with:

Yechhh!

Now let's consider a variation on this theme: repeaters.

Observable component-repeater pattern

A repeater takes a message as is, verbatim, and sends it along to potentially some number of objects.

So what is a repeater? A repeater first acts as an observer; then it passes along the news, as an observable.

Figure 5-. A repeater repeats a notification to its own list of observers (single thread)

Why use a repeater? If want to change from one observer to another, yet continue to use the same list of observers, a repeater makes that change over much simpler.

Figure 5-. The motivation for using a repeater

Here's the strategy:

"Repeater" Strategy: Use a repeater when you need to build a standard list of observers, so you can change observables and still use that standard list as-is.

We already have an IObservable interface. Now we need an IRepeat interface, a combination of both IObservable and IObserver interfaces. The resulting object model looks like this:

Figure 5-. A repeater is a combination of both observable and observer

A scenario view? Well, we'll need two sections:

- Setup

Add an iRepeat object and an iObserver object -to- an iObservable object.

- Notification

An iObservable notifies its iRepeat objects.

Each iRepeat object notifies its iObserver objects.

Each iObserver object gets what it needs from its iObservable.

Each iObserver acts accordingly.

Sometimes it's helpful to begin with a preliminary scenario sketch, before working out dynamics in detail with a scenario view. Here is such a sketch:

Figure 5-. A planning sketch for an upcoming scenario view

And the scenario view itself:

Figure 5-. Using a repeater

In Java, it looks like this:

public interface IRepeat extends IObservable, IObserver {}

public class IRepeatImplementer extends Object implements IRepeat {

#

// attributes / private / object connections

private ObservableComponent myObservableComponent = new ObservableComponent();

// methods / public / IRepeat implementation

public void addIObserver(IObserver anIObserver) {

this.myObservableComponent.addIObserver(anIObserver); }

public void deleteIObserver(IObserver anIObserver) {

this.myObservableComponent.deleteObserver(anIObserver); }

public void deleteIObservers() {

this.myObservableComponent.deleteIObservers(); }

public void update(Object theObserved, Object changeCode) {

// my update is to notify my iObservers with these parameters

this.myObservableComponent.notifyIObservers(theObserved, changeCode); }

#

}

Code notes: We could use a generic name for this class like Repeater . An IRepeatImplementer object behaves much like a component since it just passes along the update parameters to its own ObservableComponent object.

Threaded-observable component

So far, we've been working with s single-thread solution, like this:

Figure 5-. Active notification, with a single thread

Yet we could run notification on a different thread:

Here is what it might look like:

Figure 5-. Active notification, with an observable component spawning a separate notification thread

The notification thread can run as long as it needs to, as long it has updates to take care of. When no more updates are pending, then an observable component can kill that notification thread.

Yes, it's time for an object model-this time, with a threaded observable component:

Figure 5-. Adding a threaded-observable component

The following scenario view includes: (1) setup, (2) an observable thread, and (3) a notification thread. Check it out:

Figure 5-. A threaded observable component spawns a separate thread, so it can wind its way through the notifying of observers

So far, so good. Yet what happens when a single threaded-observable component receives a another notifyIObservers message, even before the first one is done?

Here is a more detailed look at a threaded-observable component-in an object model:

Figure 5-. A more detailed look at a threaded-observable component

The details of working with a notification queue are spelled out in the following scenario view:

Figure 5-. Detailed interactions for a threaded-observable component

Take a closer look at those sync blocks. The first sync makes sure that just one notification thread is spawned. The second sync makes sure that the observable thread, which at some points invokes setStatus, is kept from interfering while the notification thread runs through getStatus. The third sync makes sure that we can stop (kill) a notification thread without some other thread getting in and adding a notification request at the same time.

What about the rest of the design? Well, yes: anyone that accesses the status attribute needs to use a sync, to ensure thread-safe access. That about does it.

In Java, it looks like this:

public class ThreadedObservableComponent extends Object implements Runnable {

#

// attributes / private

private Vector notificationQueue = new Vector();

// attributes / private / object connections

private Vector myIObservers = new Vector();

private Thread notificationThread;

// methods / public / notification

public void notifyIObservers(Object theObserved, Object changeCode) {

this.addToNotificationQueue(theObserved, changeCode); }

// methods / public / Runnable implementation

public void run() {

do { // while the notification queue is not empty

// get the observed and the change code from the notification queue

Object theObserved = this.notificationQueue.elementAt(0);

Object changeCode = this.notificationQuese.elementAt(1);

// iterate through the vector of IObservers and tell each IObserver to update

Enumeration myIObserversList = this.myIObservers.elements();

while (myIObserversList.hasMoreElements()) {

// must cast the element to IObserver

IObserver anIObserver = (IObserver) myIObserversList.nextElement();

anIObserver.update(theObserved, changeCode); } }

while (this.removeFromNotificationQueue()); }

// methods / private protected / synchronized

private protected synchronized

void addToNotificationQueue(Object theObserved, Object changeCode) {

this. notificationQueue.addElement(theObserved);

this. notificationQueue.addElement(changeCode);

if (this.notificationQueue.size() == 2) {

// the queue was empty so create an start a thread

this.notificationThread = new Thread(this);

this.notificationThread.start(); } }

private protected synchronized

boolean removeFromNotificationQueue() {

// remove first two elements from the notification queue

this. notificationQueue.removeElementAt(0);

this. notificationQueue.removeElementAt(0);

if (this.notificationQueue.size() == 0) {

// the queue is empty so kill the thread and return false

this.notificationThread.stop();

return false; }

return true; // queue is not empty so return true }

#

}

Code notes: There are many ways to implement the notification queue. We decided to use a vector so that we can add new notifications to the end of the queue and can remove old notifications from the beginning of the queue.

Active notification: conclusion

Observable-observer is vital means of active notification. In a nutshell:

Stay away from Java's Observable class and Observer interface. The former encourages an inappropriate use of inheritance (too susceptible to change over time). The latter is hardwired to working with Observable objects (needlessly limiting)

Apply the "observable component-IObserver" pattern.

Add a repeater when you need to keep changing the observable that a list of observers works with.

Add a multithreaded observable component, when you need to run notification on a separate thread.

Summary

In this chapter, you've worked with notification, how lets other objects know that something significant has happened.

Passive notification is simple yet resource-intensive. Timer-based notification is a useful pattern. Active notification is most interesting; it's an essential ingredient for problem-domain object reuse; it's an essential ingredient for designing loosely-coupled subsystems.

Together, we explored three major notification mechanisms:

The strategies you learned and applied in this chapter are:

"Holder-Interface" Strategy: Establish a collection; define an interface.

"Balanced Design" Strategy: Design at one extreme, another extreme, and then somewhere in between. Design connotes looking at alternative and picking a reasonable approach.

"Extracting Interfaces from a Class Hierarchy" Strategy: When you find that a subclass is inheriting one or more methods that are merely method signatures, use an interface for those method signatures (that's exactly what an interface is for).

"Don't Limit Your Interfaces with Needless Assumptions" Strategy: Type your interface parameters as a built-in type (for example: int, float, String, StringBuffer, Object). Let each implementer of that interface test for the specific classes of objects it works with. Reason: increase the likelihood of reuse of each interface.

"Message Inward, Notify Outward" Strategy:

UI-invoked changes: message inward, from UI to PD.

PD-invoked changes: notify outward, from PD to UI.

cover Back to internet programming Links (ip-Links) page
Back to Java OO Design & Coding Standards (joodcs) page
Back to "Java Design" book page
Back to Table of Contents
Back to Chapter 4
Source http://www.oi.com/5.htm

Home | Search | What's New | Workshops | Books
Software | Support | Free Goodies | Newsletters | Feedback