January 10, 2012

Observer: Part IV - Generic Observer

This is continuation from Observer: Part III - Java EventListenerList.

Sorry for the delay. I ditched the original ending for this series and did not have time to write a new one until now. I was planning to finish by presenting you generic versions of the original Java Observer/Observable and teaching how to use them. But I changed my mind, since the interesting part about generic observer is not how to use it, but how to write it. And that is the main issue today.


Warning for the beginners! 

If you have been reading through the previous parts and are here to learn how to use the observer pattern, the generic observer is not really the best way. You are better off by sticking to the stuff that you can find in the previous parts. While making the pattern implementation generic does solve some of it's problems, it does not change the fact that the Observer is a concrete class that you have to inherit. Also, you would need to take these classes into your own code base, to keep and take care for the rest of your application's life.

Experience is about knowing when it's best to keep things simple, and when it's worth to drive for a perfect solution that is often more complex. We are dealing with a simple pattern here, why not keep the solution simple too?


You have been warned. The perverts who want The Perfect Observer Pattern may continue...

If you really, really want to bring in some complex stuff to your code-base, there is even MORE complicated solution you can use for the observer pattern. In his blog, Steven Jeuris's has solved the same problem by using dynamic proxies. The solution is beautiful and uses both generics and reflection. Just a type of thing your average maintenance person is going to loooove! Just don't use it in performance-critical places as doing things through reflection is quite slow.

Steven also promotes PerfectJPattern that has generic implementations for several design patterns. Each pattern  is nicely explained with code examples, see the Observer, for example. I just stumbled into that, but it might be interesting to check how it does things and if it's good, maybe run it through the legality checks to see if it's published under a licence that the company I work with would allow. Most big companies cannot just allow you to bring in new third-party libraries without legality-check. The reason is that some open-source licences make their user applications open-source too.


Prerequisites

While the previous parts of this series have been beginner-level, this one is not. It's not that this would be much harder to understand, just that you need some background information. So if you are a beginner, I suggest that you go through this Java lesson on Generics before continuing.

If you are an expert in generics, you are going to have some good laughs on my behalf.

If you just stumbled in without reading the previous parts, you might want to check the Java implementation of the Observer interface and an Observable class to go with the interface. Those are the classes that we are rewriting here.

Are you ready? Let's start.


Defining the signature

When you start designing generic classes, it's always best to begin by thinking about their signatures. In this case the question is - how many generic arguments do we need here?

Fortunately, the answer can be found in the book Java Generics and Collections, page 137. It gives us the following signatures:

public class Observable<S extends Observable<S, O, A>, O extends Observer<S, O, A>, A>

public interface Observer<S extends Observable<S, O, A>, O extends Observer<S, O, A>, A>

That is nice.... huh? S stands for Subject, O for Observer and A as Argument.

It does come with a warning: "So you might wonder: could the declarations be simplified by dropping the type parameter S from Observable and the type parameter O from Observer? But this won't work, since you need S to be in scope in Observable so that it can be passed as a parameter to Observer, and you need O to be in scope in Observer so that it can be passed as a parameter to Observable".

I guess I should have read the first 137 pages also, but I was just googling around and I did not understand the warning. There was no further explanation and I did not see any obvious reason why the extended classes should be in scope. So my little caveman brain told me: "Me more smart, me does this better, more simple".

What IF we try to do this with one generic argument only? That would give us the following signatures:

public class Observable<A>

public interface Observer<S extends Observable<A>, A>

Yeah, yeah, I know it won't work, but let's just play we don't know it yet.


Trying out with one generic argument

Here is the signature for Observer's update method:

void update(S observable, A arg);

At the same time Observable would manage its Observers like this:

public void addObserver(final Observer<? extends Observable<A>, A> observer)

As we implement our Observable we will stumble into a problem in notifyObservers(A arg) method. We need to call the update(S observable, A arg) method of the Observer, but it does not accept our Observable as an argument. Why not, the definition for S was <S extends Observable<A>>? Typing is very strict in generics and the Observer wants something that extends the Observable. Observable extends Object, so based on the generics rules it is not valid argument for update method. I have not yet found syntax in generics for saying: "<? extends Something, but Something will do too>", and that problem actually does not go away by adding more generic arguments. So I just ended up using plain, non-typed Observer in notifyObservers(A arg) method in order to be able to pass Observable.this as an argument.

But this little incident draws our attention to typing. Our Observer accepts items that are of type <? extends Observable<A>>. So is it possible that we end up passing incompatible argument to update method? Actually, in this one generic argument solution, it is. Take a look at this:

    public class Koala extends Observable<Boolean> {

        public void setHungry(final boolean isHungry) {
            setChanged();
            notifyObservers(isHungry);
        }

        @Override
        public String toString() {
            return "koala";
        }
    }

    public class Tiger extends Observable<Boolean> {

        public void setAngry(final boolean isAngry) {
            setChanged();
            notifyObservers(isAngry);
        }

        @Override
        public String toString() {
            return "tiger";
        }
    }

    public interface KoalaObserver extends Observer<Koala, Boolean> {

    }

And here is our test case that shows the problem:

    public void testMixedObservers() {
        final Tiger tiger = new Tiger();
        final KoalaObserver koalaObserver = new ZooKeeper();

        tiger.addObserver(koalaObserver);
        try {
            tiger.setAngry(true);
        }
        catch (final ClassCastException ex) {
            assertEquals(
                    "org.beyondhc.lessons.observer.genericwithonearg.GenericObserverTest$Tiger"
                            + " cannot be cast to "
                            + "org.beyondhc.lessons.observer.genericwithonearg.GenericObserverTest$Koala",
                    ex.getLocalizedMessage());
        }

    }

As you can see, we can add KoalaObserver to Tiger. Tiger will happily accept any Observer that extends Observable<Boolean>. As we cannot check for the correct type, we get an ugly ClassCastException. This is definitely to the worse from the original solution. It is clear now what the warning in the book meant - unless the Observer knows the Observable and vice versa, we cannot be sure that the user does not make similar mistakes than with the non-typed variants. Although I was not able to repeat this particular problem with a version that uses two generic arguments, I am pretty sure someone smarter than me can get it broken too. Thus, let's forget trying this with less arguments and return to the three-argument version.


Observable and Observer with 3 generic arguments

When the signatures are set, the rest of the implementation is not that hard. But the signature does look complex, so you might be wondering about the usage. And doesn't it make this thingy really non-flexible if Observable is so tightly coupled with its Observer?

It turns out that this tight coupling actually forces you to better design. First, it makes it more tempting to implement a separate interface for the Observer:

public interface TigerObserver extends
            Observer<Tiger, TigerObserver, Boolean> {

    }

And secondly, as the Observer can implement only one interface, you are forced to composition if you want to observe more than one type of items:

    public class ImprovedZooKeeper {

        private final TigerObserver tigerObserver;

        public ImprovedZooKeeper() {
            this.tigerObserver = new TigerObserver() {

                @Override
                public void update(final Tiger observable, final Boolean arg) {
                    // Do your actions here
                }
            };
        }

        public void observerTiger(final Tiger tiger) {
            tiger.addObserver(tigerObserver);
        }

    }

And the composition effectively hides the update method so that only Tiger may call it.

The Observer and Observable discussed in this article can be found in the BeyondHc util package.

The broken ones with 1 and 2 generic arguments are available too. They can be found on the test side of the BeyondHc, in packages
observer.genericwithonearg
and
observer.genericwithtwoargs.