Posts tagged ‘observer’

Observer patterns is one of the core design patterns that every developer should know and use. It let’s you create components with low coupling and adhere to Hollywood principle.

Some pseudo-code for adding new observer might look like this:

void addObserver(String ev, Observer ob);
producer.addObserver("myEventName", new Observer(){public void observe(Producer producer){}});

But there are at least 2 issues with this design:

  • You have to explicitly define event name
  • You can only pass reference to a producer object.
You could of course decide that a producer can only dispatch one type of events and we don’t need to explicitly name them, but usually that’s not the case.

So what if you wanted to define your events using some kind of interface, eg:


public interface MyEvent{}
producer.addListener("myEventName", new Observer<MyEvent>(){public void observe(MyEvent me){}});

So this is a little bit better but you still need to pass name of event as String.

So maybe we could use java generics to use power of type-safety at compile time and still have all the benefits of low-coupling. Something that could look like this:


public <T> void addListener(Class<T> e, Observer<T> o);
producer.addListener(MyEvent.class, new Observer<MyEvent>(){public void observe(MyEvent me){}});

Where first parameter could be used as EventType declaration.

This is very close but has still one flaw – it forces redundant code:

  • once event type is passed explicitly as event definition
  • second during observer creation – template parameter

So maybe it could be somehow simplified into something like this:

producer.addListener(new Observer<MyEvent>(){public void observe(MyEvent me){}});

Some of you might say that this is impossible in Java due to type-erasure. This is all true – but there’s second part of it – generic types are available at runtime through聽getGenericSuperClass/getGenericInterfaces methods.

You can see source code for my type-safe event system on github but I think it needs just some clarification – why do you need that ugly MethodProxy class.
So after writing DefaultImplementation of event dispatcher interface I found out that compiler would not allow call to Listener’s on method with passed event instance. So I decided to find proper method using reflection and only internally resign of type-safety.
MethodProxy class creates proxy upon instantiation so it will report any problems very close to their cause.

So here’s what you can find already in the repo:

  1. Dispatcher interface with default implementation
  2. Simple event interface used as entry point for all events
  3. Simple listener interface used as entry point for all listeners
I guess there might be sample usage scenario:
Declare your event:
</pre>
public class MyEvent聽implements ProcessorLifecycleEvent{}
Create event dispatcher and register your listener:
ProcessorLifecycleEventDispatcher eventDispatcher = new DefaultProcessorLifecycleEventDispatcher();
eventDispatcher.registerListener(new ProcessorLifecycleEventListener<MyEvent>(){
public void on(MyEvent e) {
// some logic here
}
});

Publish new event:

eventDispatcher.dispatch(new MyEvent(){});
In the sample above MyEvent is very simple but it could take some data through constructor and act as full-blown DTO聽which simplifies greatly interactions beacause listener code doesn’t have to do any runtime casts – see example.
The whole project is part of another thing – Liferay service without service builder, which I’m going to describe soon 馃檪

So enjoy.

Dzisiaj czas na przyk艂ad do艣膰 popularnego wzorca projektowego, czyli Obserwatora.

Problem, kt贸ry b臋d臋 tutaj opisywa艂 dotyczy przechowywania referencji do obiektu w HashMapie. Obiekt jest do艣膰 prosty i zawiera tylko 2 w艂asno艣ci, numer i nazw臋, oto przyk艂ad:


class MyObject {

private String name;

private int number;

}

呕eby nie gmwatwa膰 kodu pomin臋 gettery i settery.

Do przechowywania obiekt贸w wykorzystam r贸wnie prosty pojemnik, umo偶liwiaj膮cy dodawanie gotowych obiekt贸w i pobieranie ich wg nazwy, klasa nazywa si臋 MyObjectContainer:


class MyObjectContainer {

private Map&lt;String,MyObject&gt; 聽myObjectsMap = new HashMap&lt;String,MyObject&gt;();

// 2 proste metody do wstawiania i pobierania obiekt贸w

public MyObject get(String key){聽// specjalnie nie implementuje interfejsu java.util.Map, bo tam by to nie przesz艂o

return myObjectsMap.get(key);

}

public MyObject putSimple(MyObject myObject){

return myObjectsMap.put(myObject.getName(), myObject);

}

}

I w ten prosty spos贸b doszli艣my do problemu, kt贸ry chcia艂em opisa膰, a zdarza si臋, gdy pobranemu na zewn膮trz obiektowi MyObject zostanie zmieniona nazwa. W mapie pozostanie stary wpis, natomiast referencja b臋dzie mia艂a nowa nazw臋. Pr贸ba pobrania elementu wg. nowej nazwy sko艅czy si臋 albo nies艂awnym NPE, albo (co gorsza) pobraniem innego elementu, kt贸ry przypadkiem ma tak膮 sama nazw臋. W obydwu przypadkach nie jest za dobrze. Kod ilustruj膮cy znajduje si臋 ponizej:


MyObject myObject = new MyObject("name", 1);

myObjectContainer.putSimple(myObject);

MyObject myObject = myObjectContainer.get( "name" );

myObject.setName( "newName" );

myObjectContainer.get( "newName" );  // i tu jest problem

Jak wida膰 ostatnia linijka oznaczona „tu jest problem”, spowoduje b艂膮d, gdy偶 obiekt o nazwie „name” zmieni艂 nazw臋 na „newName” natomiast w mapie pozostanie dost臋pny jedynie po kluczu „name”.

呕eby temu zapobiec skorzystamy z wzorca obserwator w艂a艣nie i w momencie zmiany nazwy obiektu, nasz MyObjectContainer zostanie powiadomiony przez samego zainteresowanego, co da mu mo偶liwo艣膰 zaktualizownia w艂asnego stanu. Do tego jednak potrzebujemy lekko zmodyfikowa膰 nasz przyk艂ad. Po pierwsze obiekt MyObject musi mie膰 mo偶liwo艣膰 notyfikowania o zmianie swojego stanu, skorzystam tutaj z implementacji Obserwatora /Obserwowanego dost臋pnej w pakiecie java.util :


class MyObject extends Observable {

public void setName( String newName ) {

String oldName =name;

name = newName;

if( !newName.equals( oldName ) ) {

setChanged();

notifyObservers(oldName); 聽// przekazemy stara nazwe, zeby dalo sie mape zaktualizowac

}

}

}

Teraz wystarczy tylko doda膰 mo偶liwo艣ci „nasluchiwania” do聽MyObjectContainer:


class MyObjectContainer implements Observer {

public MyObject putSimple(MyObject myObject){

myObject.addObserver( this );

return myObjectsMap.put(myObject.getName(), myObject);

}

// no i jeszcze aktualizacja stanu

public void update(Observable o, Object arg) 聽{

String oldName = (String) arg;

MyObject myObject = (MyObject) o;

if( !oldName.equals(myObject.getName())){

MyObject oldObject = myObjectsMap.put(myObject.getName(), myObject);

myObjectsMap.remove(oldName);

if( oldObject != null ) { // to znaczy, ze cos pod t膮 nazw膮 ju偶 by艂o

}

}

}

Jak wida膰 kontener dostaje star膮 nazw臋 dzi臋ki kt贸rej mo偶e usun膮膰 odpowiedni wpis, a tak偶e zmodyfikowany obiekt MyObject, co pozwala mu doda膰 referencje pod now膮 nazw膮. Teraz ju偶 mo偶na zmienia膰 nazwy w te i wewt臋, coprawda pozosta艂a jeszcze implementacja zachowanai w przypadku znalezienia takiego samego obiektu w mapie, no ale to zostawi臋 ju偶 jako zadanie domowe dla czytelnika.