The Observer is known as a behavioral pattern, as it’s used to form relationships between objects at run-time. The definition provided in the original Gang of Four book on Design Patterns states:
Defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
The Java library implements a non-generic version of the Subject-Observer pattern in the package java.util with the class Observable and the interface Observer. The Observable class contains methods to register observers (addObserver), to indicate that the observable has changed (setChanged), and to notify all observers of any changes (notifyObservers), among others. The notifyObservers method may accept an arbitrary argument of type Object that is to be broadcast to all the observers. The Observer interface specifies the update method that is called by notifyObservers. This method takes two parameters: the first one, of type Observable, is the subject that has changed; the second, of type Object, is the broadcast argument.
We could, of course, create our own classes and not use those provided by the library, but isn’t it wonderful when someone else takes care of maintaining our code? So we’re going to use the provided library for this example.
Let’s see an example. Suppose we have a family with 4 members, mom, dad, a baby and a dog. When the little baby cries, mom changes his diaper, so we have the class Baby as an Observable and the class Mother as an Observer of the Baby:
public class Baby extends Observable {
private String name;
public Baby(String name) {
this.name = name;
}
public void babyIsCrying() {
setChanged();
notifyObservers(name);
}
}
public class Mother implements Observer {
@Override
public void update(Observable subject, Object param) {
String babyName = (String) param;
System.out.println(“Let’s change that diaper on “ + babyName + “!”);
}
}
The father takes the dog for a walk when it barks, so the class Dog is another Observable and the class Father its Observer:
public class Dog extends Observable {
private String name;
public Dog(String name) {
this.name = name;
}
public void barks() {
setChanged();
notifyObservers(name);
}
}
public class Father implements Observer {
@Override
public void update(Observable o, Object arg) {
String dogName = (String) arg;
System.out.println(dogName + “, let’s go to a walk!”);
}
}
At a first glance, everything seems to be working ok, but What if somebody understands wrongly the relationships and adds the Mother as an Observer of the Dog(the compiler wouldn’t complain about it and the runtime will sillently change the diaper on the dog and wouldn’t even notify us about the horrible mistake we did).
Let’s test it:
public class TestingLegacy {
public static void main(String[] args) {
Baby david = new Baby(“David”);
Mother mom = new Mother();
Dog rover = new Dog(“Rover”);
Father john = new Father();
// test mother-baby relationship
david.addObserver(mom);
david.babyIsCrying();
// test father-dog relationship
rover.addObserver(john);
rover.barks();
// delete observers to test wrong relatinships
david.deleteObservers();
rover.deleteObservers();
System.out.println(“Add a wrong relationship and test.”);
// add the father as the baby’s observer
david.addObserver(john);
// add the mother as the dog’s observer
rover.addObserver(mom);
david.babyIsCrying();
rover.barks();
}
}
The console outputs:
Let’s change that diaper on David!
Rover, let’s go to a walk!
Add a wrong relationship and test.
David, let’s go to a walk!
Let’s change that diaper on Rover!
To ensure that a Subject-Observer relationship is well established, we should use one of the nicest Java 5 feature: generics. They add stability to the code by making more of your bugs detectable at compile time. But there is a small problem, because Observable and Observer are part of the java docs, we can’t and shouldn’t change them. So to be able to add bounds, we will create stub classes with generic signatures but no bodies. We compile the generic client against the generic signatures, but run the code against the legacy class files. This technique is appropriate when the source is not released, or when others are responsible for maintaining the source.
So we can replace Observable and Observer with the type parameters S and O (for Subject and Observer). Then within the update method of the observer, you may call on any method supported by the subject S without first requiring a cast. Also, The appearance of Object in a method signature often indicates an opportunity to generify. So we should add a type parameter, A, corresponding to the argument type.
Here is our first version of stubs with two generic params:
public class Observable<O extends Observer<?, A>, A> {..}
public interface Observer<S extends Observable<?, A>, A> {…}
Now when creating a Mother-Baby relationship things work just fine. What about Mother-Dog? the compiler let’s us do the link, the runtime however throws a ClassCastExeption for this time.
class WrongBoundedBaby extends Observable<WrongBoundedMother, String>
class WrongBoundedMother implements Observer<Dog, String>
Exception in thread “main” java.lang.ClassCastException: bounds.WrongBoundedBaby cannot be cast to bounds.Dog
at bounds.WrongBoundedMother.update(WrongBoundedMother.java:1)
at java.util.Observable.notifyObservers(Observable.java:142)
at bounds.WrongBoundedBaby.babyIsCrying(WrongBoundedBaby.java:28)
at bounds.TestingLegacy.main(TestingLegacy.java:38)
So, not good enough, as the bug won’t be detected until run-time. We need S to be in scope in Observable so that it can be passed as a parameter to Observer, and we need O to be in scope in Observer so that it can be passed as a parameter to Observable.
class Observable<S extends Observable<S, O, A>, O extends Observer<S, O, A>, A>
interface Observer<S extends Observable<S, O, A>, O extends Observer<S, O, A>, A>
This is the last version of our classes:
class Baby extends Observable<Baby, Mother, String>
class Mother implements Observer<Baby, Mother, String>
class Dog extends Observable<Dog, Father, String>
class Father implements Observer<Dog, Father, String>
// COMPILE- error when adding the father as the baby’s observer
// david.addObserver(john);
// COMPILE- error when adding the mother as the dog’s observer
// rover.addObserver(mom);
The Java Generics and Collections book defines this type of bound as mutually recursive.
In conclusion, the Observer is a great design pattern with many implementations, but a junior dev can be easily tricked to use it wrongly. Adding the mutually recursive bounds helps us idetifying the problem at compile time and sleeping good at night when knowing that we did a great job.
This post is part of the Java Advent Calendar and is licensed under the Creative Commons 3.0 Attribution license. If you like it, please spread the word by sharing, tweeting, FB, G+ and so on!
2 Pingbacks