5

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

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
whoshotdk
  • 286
  • 2
  • 14
  • Which methods of `Dog` have references to the view? I don't see any. – SJuan76 May 11 '13 at 14:53
  • The best way is to use an interface, which the video tutorial suggests. Is listener an interface in your implementation? – Kevin Bowersox May 11 '13 at 14:56
  • SJuan76 - the references I am talking about are the calls to listener.onDogHungry and listener.onDogNeedsToPee in the Dog 'fire' methods. – whoshotdk May 11 '13 at 15:31
  • Kevin - the listener is in fact an interface, just like the tutorial suggests. However at the end of the tutorial I am still left with calls to the listener methods from the observable methods. – whoshotdk May 11 '13 at 15:32

2 Answers2

4

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.

Boris the Spider
  • 59,842
  • 6
  • 106
  • 166
  • I like this answer for its clarity, thank you Boris. I dont *particularly* like the case statement there which needs to check what kind of event happened, but I guess it doesnt just happen by magic :D I will give this a go with a couple of observables/listeners and see how I get on. I'll accept this answer at some point today if I can wrangle up something reasonably efficient with it! – whoshotdk May 11 '13 at 15:39
  • Ah I just realised this is a much better implementation of what I tried earlier in my attempts; except I was using reflection to invoke methods based on string name instead of an event object. I think this is going to work :D – whoshotdk May 11 '13 at 15:41
  • The `enum` was just a suggestion - you could use a visitor pattern to fully leverage polymorphism. – Boris the Spider May 11 '13 at 15:41
1

For MVC purposes (especially for MVP) I have gathered good experiences with Event bases programming. So you would need an EventBus which will be used by Dog and Owner. The Owner will subscribe for certain Event classes like HungerLevelIncrease and the dog will fire such Events. For your Dog Owner example this would be kinda strange because the Owner did not know his dogs but for GUI programming this is a nice and easy solution to decouple things especially between other Controllers.

You could easily create an Bus by your own or you can use the one from google guava.

This is the best I way I know to create a really loosely coupling.

mszalbach
  • 10,612
  • 1
  • 41
  • 53
  • This did initially seem like a good idea to me but after a little reading others have noted that this pattern essentially makes every observable/listener need to know about one bus. Adding new event types would mean changing that bus too. See: http://stackoverflow.com/questions/3987391/why-people-use-message-event-buses-in-their-code. Maybe that isnt as bad as it seems, idk. – whoshotdk May 11 '13 at 15:37
  • 1
    @user2373021 have you looked at something like [mbassador](https://github.com/bennidi/mbassador) - it's annotation based so all you need is to annotate your listeners and it will deduce what they want to listen to from the method parameters. It can also send messages asynchronously... – Boris the Spider May 11 '13 at 15:43
  • @user2373021 its true that you need to add the bus to all classes needing him. However most of the time you will add a dependency injection container anyway to bigger applications which can inject you eventBus for you. But adding new event types should not affect your bus at all. If you have only have a few classes I would go for the simple listener solution too but if you get more dialogues and have multiple dialogues talk to each other and with other classes (e.g. server). I would give the EventBus mechanism a try. – mszalbach May 11 '13 at 16:06