Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Loosely coupled observer pattern

I realise this subject has been covered to death but I am still struggling and could do with some specific help.

My aim is to implement a simple Observer pattern between some kind of observable (lets say a Dog) and some kind of listener (lets say Owner).

Eventually the Owner would be a 'view' and the Dog a 'model' in a MVC paradigm. I am using Dog and Owner just to try and simplify things here.

I have attempted to use Java's built in Observer / Observable classes but have realised how bad the Observers update() method is - it receives a POJO and I would need to check / cast that POJO in the update() method. I would much prefer to have my 'update()' method receive something it can expect.

So, I followed a few tutorials, including this one which uses the Dog/Owner as an example:

http://www.youtube.com/watch?v=qw0zZAte66A

Here I have been shown how to roll my own Observer/Observed classes. In pseudo code, what I now have is this:

Dog/Model {

    List listeners;

    public fireDogHungryEvent() {

        foreach listener {
            listener.onDogHungry(this);
        }
    }

    public fireDogPeeEvent() {

        foreach listener {
            listener.onDogNeedsToPee(this);
        }
    }

    public getHungerLevel() { return hungerLevel; }
    public getBladderCapacity() { return bladderCapacity; }
}

Owner/View {

    public onDogHungry(model) {
        println(model.getHungerLevel());
    }

    public onDogNeedsToPee(model) {
        println(model.getBladderCapacity());
    }
}

So now rather than one update() method, I have methods that handle specific events. Brilliant. I am currently happy with the Owner/view class. It knows about the Dog/model's methods and that is fine (I think).

What I do not like is that the Dog/model has references to methods in the Owner/view. I've read countless times and completely agree that a model should not be tightly coupled to its views such as it seems to be above. I am also not keen on all the 'fire' methods in the Dog/model doing nearly the same thing; looping over all it's listeners and just calling a different method on each listener.

Is it possible to decouple this relationship further and not have the Dog/model call specific methods? If so, what is the best way to go about receiving Dog/Model data into the Owner/view and working with it appropriately?

Thanks

like image 384
whoshotdk Avatar asked May 11 '13 14:05

whoshotdk


1 Answers

You should interface away knowledge of specific implementation from both the Observer and the Observable

public enum EventType {

    HUNGRY,
    PEE;
}

public interface DogEvent {

    EventType getType();
}

public interface DogListener {

    void fireEvent(DogEvent event);
}

public class Dog {

    private final Set<DogListener> listeners = new CopyOnWriteArraySet<DogListener>();

    public void register(final DogListener dogListener) {
        listeners.add(dogListener);
    }

    public void unregister(final DogListener dogListener) {
        listeners.remove(dogListener);
    }

    public void firePeeEvent() {
        fireEvent(new DogEvent() {
            @Override
            public EventType getType() {
                return EventType.PEE;
            }
        });
    }

    public void fireHungryEvent() {
        fireEvent(new DogEvent() {
            @Override
            public EventType getType() {
                return EventType.HUNGRY;
            }
        });
    }

    private void fireEvent(final DogEvent dogEvent) {
        for (final DogListener listener : listeners) {
            listener.fireEvent(dogEvent);
        }
    }
}

public class Owner implements DogListener {

    @Override
    public void fireEvent(DogEvent event) {
        switch (event.getType()) {
            case PEE:
                System.out.println("Someone take the dog out");
                break;
            case HUNGRY:
                System.out.println("I can't believe the dog is hungry _again_!");
                break;
        }
    }
}

In this case the Dog does not know about the implementation of the Owner it just known that the Owner is a DogListener.

The Owner on the other hand does not know about the Dog it just knows that it has an incoming DogEvent.

like image 77
Boris the Spider Avatar answered Sep 26 '22 03:09

Boris the Spider