WatchingtheObservables[1]

download WatchingtheObservables[1]

of 11

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