WatchingtheObservables[1]
-
Upload
caterina-carbone -
Category
Documents
-
view
229 -
download
0
Transcript of WatchingtheObservables[1]
-
7/28/2019 WatchingtheObservables[1]
1/11
Watching the Observables: The
Dependency Mech anism
John HuntArkevista Ltd,
Hartham Park
Corsham,
Wiltshire
SN13 0RP
THE DEPENDENCY MECHANISM
This column presents the way in which Java implements the dependency mechanism
found in Smalltalk also known as the Observer pattern (in the "Patterns" book). There
are in fact a number of different relationships between objects in an Java system. Most
of these are familiar to any student of object oriented programming languages:
Inheritance (class to class relationships)
Instantiation (class to instance relationships)
Part-ofor contains (instance to instance relationships)
However, there is another important relationship supported by many object oriented
languages, such as Smalltalk and Java. This is the dependency relationship, where
the state or behaviour of one object is dependent on the state of another object. For
example, in figure 1 the arrows indicates that there is a set of dependency
A
B
D
C
E
F
Tail depends
on head
FIG..1 Dependency between objects
-
7/28/2019 WatchingtheObservables[1]
2/11
relationships between the objects A to F. Object A is dependent on some aspect of
objects B and D. In turn object B is dependent on some aspect of object C and so on.
Why Do We Want Dependency?
The reasons for dependency are all down to change. We wish to communicate the fact
that an object has changed its value to another object which may be interested in
either the fact of the change or the new value effected by the change. The dependency
mechanism provides a way of communicating such events in a generic,
implementation independent, manner.
An obvious question is why not just get the object to send messages to those
interested in it? The answer is that if you know the objects that are interested, then
you can send messages to them. However, if all you know is that sometime, at a later
date, some object may need to know something about the state of an object (but you
do not know what that other object might be) then you cannot arrange to send
messages to it.
The dependency mechanism allows any object whose class is a subclass of
Observable to act as the source of a dependency. Any object that implements the
Observer interface can act as the dependent object.
We do not need to know what might be interested in the object. We merely need to
know that it might be involved in a dependency relationship. The (hidden) dependency
mechanism takes care of informing the unknown objects about the updates.
How Does Dependency Work?
The dependency mechanism is implemented in the class Observableand the interface
Observer within thejava.util package. This class is a direct subclass ofObject,
so any class can inherit from Observable and thus take part in a dependency
relationship. In turn, a Java class can implement zero of more interfaces, thus an
observer class can implement the Observer interface and receive notifications of
changes to the Observable object.
In Java terminology, the head of the dependent relationship (i.e. the object on which
other objects depend) is referred to as the observable object, while the dependent
object is referred to as the observer object. The observable object allows other objects
to observe its current state. An observable object can have zero or more observers,
which are notified of changes to the objects state by the notifyObservers method.
-
7/28/2019 WatchingtheObservables[1]
3/11
You can browse the Observable class to explore the dependency mechanism. The
basic implementation, inherited from Observable, associates a vector of other objects
with the observable object. This vector holds the objects which are dependent on the
object (collectively, these objects are known as the objects observers). For example,
in Figure 2, the object ObjectA has two observers ObjectB and ObjectC. The links
to the dependent objects are held by ObjectA in a list of observers called obs.
ObjectA cannot access this vector as it is private to the Observable class. However,
it can obtain the number of observers using the countObservers method.
obs
ObjectAObjectCObjectB
FIG. 2 An object and its observers
Constructing Dependencies
The addObservermessage adds an object to a dependency list. For example, we can
construct the above dependencies:
ObjectA.addObserver(ObjectB);
ObjectA.addObserver(ObjectC);
Duplicates cannot be held in the list of observers. If you attempt to add an object to
the list of observers more than once, it is only recorded once (and thus is only told of
changes once).
An observable object holds a vector of objects that depend on it, but an object
cannot access information about the objects on which it depends. For example, there
are no references from ObjectB orObjectC back to ObjectA in Figure 17.2. This
may seem a bit strange at first, however, once you understand how the dependency
mechanism works, as realized by the Observable class and the Observer interface,
you will see why things are this way round.
You can remove dependencies once they have been created. The following code
removes ObjectB from the observer list ofObjectA:
ObjectA.deleteObserver(ObjectB);
-
7/28/2019 WatchingtheObservables[1]
4/11
A SIMPLE DEPENDENCY EXAMPLE
We develop further the following very simple dependency example during the chapter.
It creates two objects and a dependency between them. The objects are instances of
the classes DataObject and ObserverObject, which are direct subclasses ofObservable and Object, respectively. Place the classes in appropriate .java files
(i.e. DataObject.java and ObserverObject.java).
import java.util.Observable;
public class DataObject extends Observable {
}
The update method is explained below, it is merely used here to allow the
ObserverObject to implement the Observer interface:
import java.util.Observer;
import java.util.Observable;
public class ObserverObject implements Observer {
public voidupdate(Observable o, Object arg){
System.out.println("Object " + o + " has changed");
}
}
We now have two classes that can take part in a dependency relationship. To
illustrate how objects of these classes can be related, define the following class in a
file called TestHarness.java. Although, in general, I do not condone using separate
objects to illustrate how other objects work, in this case, it ensures that you understandthat the dependency relationships are handled automatically via the inherited facilities.
public class TestHarness {
public static voidmain(String args []) {
TestHarness t = new TestHarness();
t.test();
}
public voidtest () {
DataObject temp1 = new DataObject();
ObserverObject temp2 = new ObserverObject();
temp1.addObserver(temp2);
System.out.println(temp1.countObservers());
}}
The result ofprintln is the value 1, although ourDataObject class is empty!
From the point of view of the DataObject class, dependency is an invisible
mechanism that works behind the scenes. Of course, this is not really the case. The
-
7/28/2019 WatchingtheObservables[1]
5/11
dependency mechanism has been inherited from Observable and is implemented via
message sends and method executions just like any behaviour provided by an object.
MAKING DEPENDENCY WORK FOR YOU
We have now considered how to construct a dependency relationship. We want this
relationship to inform the dependent objects that a change has occurred in the object
on which they depend.
To do this we use two sets of methods. One set, the changed methods, states that
something has changed. The other set, the update methods, is used to state what
type of update is required.
4. updatemessage3. update
message
obs
ObjectA
ObjectCObjectB
1. setChanged();
2. notifyObservers();
FIG.3 The dependency mechanism in action
Figure 3 illustrates the sequence of messages which are sent in response when an
object changes. That is, when ObjectA is sent the setChanged and
notifyObservers messages (usually by itself) all its observers are sent an update
message. From the point of view ofObjectA, much of this behaviour is hidden. In fact,
so much so that a point of confusion relates to the sending of one message (the
notifyObservers message) and the execution of another method (the update
method). A programmer defining Objects A, B and C:
sends one (or more) setChanged messages to ObjectA,
sends a notifyObservers message to ObjectA,
defines an updatemethod in ObjectB and ObjectC.
The confusion stems from the need to send one message but define another.
However, if you think about how you are linking into the existing dependency
framework, it can make more sense. The change message is a message to the
dependency mechanism asking it to notify the objects dependants about a change.
The dependency mechanism is inherited and is generic across applications, but
-
7/28/2019 WatchingtheObservables[1]
6/11
system developers cannot know when the change message should be sent that is
application specific. It is, therefore, the application developers responsibility to send
the change messages. For example, you may only want dependants to be told of
certain changes, such as updates to one field on an input screen, etc.
Similarly, there is no way that the system developers can know how the dependants
should update themselves. The update message could display the new value
produced by the originating object, perform some calculation, or access a database.
As the update methods are defined in an interface, they do nothing; they are abstract
methods. Nothing is said anywhere in the system about what an observer should do
when the object changes.
In the simple example above, we need to specify what ObjectB and ObjectC
should do when ObjectA changes. This requires defining update methods (as we did
very briefly in the ObserverObject example presented earlier).
The ChangedMethods
There are three messages that inform an object that it has changed and should notify
its observers:
setChanged() indicates to the observable object that something has happened
which should be passed onto any observers, next time they are notified of a change. It is
very useful to separate out specifying that something has changed from the actual
notification action. You can determine that observers must be notified at one point in an
objects execution (when data is entered), but trigger the notification at another point (at the
end of some execution cycle). Interestingly, the Smalltalk dependency mechanism does not
provide this flexibility.
notifyObservers() informs the observers that something has changed (but not
what the change was). It calls the notifyObservers(Object object)method with a null
parameter.
notifyObservers(Object object)notifies the observers that something has
changed and what the change was. This is done by sending the update message to the
objects in the obs vector. The update method takes two parameters: the current object and
the object passed into the notifyObservers method. It assumes that the change can be
represented as an object, so if the change results in the number24, it must be wrapped in an
Integer object.
-
7/28/2019 WatchingtheObservables[1]
7/11
The first point to note about the changed messages are that they are sent to the
object which has changed in some way. They inform the object that it has changed
and that this change should be passed onto any observers. The changed messages
do not effect the change or notify the observers.
The notifyObservers messages triggers off the update part of the dependency
mechanism. The only difference between the messages relates to the amount of
information provided. The simplest notification message (and the one with the least
information) is the notifyObservers() message. This can be useful when you want
to make sure that the dependants assume nothing about the object to which the
change is happening.
The way these messages are implemented is that the setChanged methodsets a
boolean flag, changed, to true. This flag is examined by the
notifyObservers(Object)method; if the flag is set to true, it notifies the objects
in the obs vector that they need to update themselves and resets the changed flag to
false. If the changed flag is already false, it does nothing.
THE OBSERVER INTERFACE
The Observer interface defines the abstract update method which must be
implemented by objects which wish to take part in a dependency :
public interface Observer {
void update(Observable observable, Object arg);
}
As with all interfaces, any concrete class that implements this interface must provide
the body of the update method. Any class implementing this interface can be
guaranteed to work with the notification methods used in an observer object.
The first parameter passed to this method is the observable object. If ObjectA is
sent a setChanged message followed by a notifyObservers message, then
ObjectB and ObjectC are sent the update message with the first parameter set to
ObjectA.
The value of the arg parameter depends on the version of notifyObservers
which was sent to ObjectA. If no parameters were sent, then the value ofarg is null.
If one parameter was sent, arg holds the parameter. This means that the developer
can decide how much information the observer object can work with.
-
7/28/2019 WatchingtheObservables[1]
8/11
EXTENDING THE DEPENDENCY EXAMPLE
This section provides an example of how the dependency mechanism works. We use
the DataObject and ObserverObject classes defined earlier.
The first thing we do is to define some instance variables (age, name and address),a constructor and an updater, age, in DataObject:
import java.util.Observable;
public class DataObject extends Observable {
String name = "";
int age = 0;
String address = "";
public DataObject (String aName,
int years, String anAddress) {
name = aName;
age = years;
address = anAddress;}
public voidage (int years) {
age = years;
setChanged();
notifyObservers("age");
}
public String toString() {
return "(DataObject: " + name + " of " + address
+ " who is " + age + ")";
}
}
The updater method, age, which sets the age instance variable, also informs itself
that it has changed (using setChanged) and that this fact should be passed onto its
dependants (using notifyObservers(age)). This is a typical usage of the
notifyObserversmessage. That is, it informs the notification mechanism about the
type of change which has taken place. It also illustrates good style: it informs this
object about the change which has taken place. It is very poor style to have an object
send an instance of DataObject the message age, followed by the changed
messages. It implies that something outside the DataObject decides when to inform
its observers.
Next we define how an instance ofObserverObject responds to the change in a
DataObject; we define an update method:
import java.util.Observer;
import java.util.Observable;
public class ObserverObject implements Observer {
-
7/28/2019 WatchingtheObservables[1]
9/11
public voidupdate(Observable o, Object arg){
if (arg == "age")
System.out.println("Object " + o +
" has changed its " + arg);
else
System.out.println("Don't know how to handle
changes to " + arg);
}
}
As this is just a simple example, all it does is print a string on the console that
reports the change. We use the TestHarness class we defined earlier to try out this
simple example. We use the new DataObject constructor to create the observable
object and the age updater to change the observable objects age:
public class TestHarness {
public static voidmain(String args []) {
TestHarness t = new TestHarness();
t.test();}
public voidtest () {
DataObject temp1 =
new DataObject("John", 34, "C47");
ObserverObject temp2 = new ObserverObject();
temp1.addObserver(temp2);
System.out.println(temp1.countObservers());
temp1.age(35);
}
}
The result of executing this class is illustrated below:
C >java TestHarness
1
Object (DataObject: John of C47 who is 35) has changed its age
The ObserverObject has been informed of the change of age, although we
did not define a method to do this directly.
In the simple example presented above, you should note (and understand) the
following points:
DataObject does not have a reference to, nor does it know anything about, anObserverObject.
The ObserverObject does not reference a DataObject internally.
The link between temp1 and temp2 is external to both objects.
It can be difficult to debug and maintain relationships that have been implemented
using the dependency mechanism (as the message chain is partly hidden). Therefore,
-
7/28/2019 WatchingtheObservables[1]
10/11
you should exercise care in its use. However, if used appropriately (and clearly
documented) the dependency mechanism is Java is a very powerful construct indeed.
SUMMARY
This column has introduced the dependency mechanism in Java as implemented by
the Observable class and Observer interface. This relationship is quite complex but
the user interface classes make extensive use of it and you should gain some
experience with it.
REFERENCES
Design Patterns: Elements of Reusable Object-Oreinted Software, E. Gamma, R.
Helm, R. Johnson and J. Vlissides, Addison-Wesley, 0-201-63361-2, 1995.
-
7/28/2019 WatchingtheObservables[1]
11/11
Observers and Observables 11
KEY POINTS
Observers and Observables support dependency relationships
Observable class is a direct subclass of Object
Observer interface allows objects to receive updates
Changed methods notify observers of changes
SetChanged method indicates if an actual change has occurred
May be difficult to maintain or debug