samedi 29 octobre 2016

Observer Pattern: Internal vs. External Registration

What is be the best choice to register an observer? I did not find anything on this subject. Mostly "push vs. pull" is discussed, but there are also a couple of options to register an observer.

public static void main(String[] args)
{
    Subject subject = new ConcreteSubject();
    // External registration
    Observer observerExternal = new ConcreteObserverExternal();
    subject.registerObserver(observerExternal);
    // Internal registration, option 1
    Observer observerInternal1 = new ConcreteObserverInternal1(subject);
    // Internal registration, option 2
    ConcreteObserverInternal2 observerInternal2 = new ConcreteObserverInternal2(subject);
}

interface Observer
{
    void inform();
}
class ConcreteObserverExternal implements Observer
{
    @Override
    public void inform()
    {
        // do sth.
    }
}
class ConcreteObserverInternal1 implements Observer
{
    public ConcreteObserverInternal1(Subject subject)
    {
        subject.registerObserver(this);
    }

    @Override
    public void inform()
    {
        // do sth.
    }
}
class ConcreteObserverInternal2
{
    public ConcreteObserverInternal2(Subject subject)
    {
        subject.registerObserver(() -> inform());
    }

    private void inform()
    {
        // do sth.
    }
}
interface Subject
{
    void registerObserver(Observer obs);

    void unregisterObserver(Observer obs);
}
class ConcreteSubject implements Subject
{
    @Override
    public void registerObserver(Observer obs)
    {
        // register
    }

    @Override
    public void unregisterObserver(Observer obs)
    {
        // unregister
    }

    private void foo()
    {
        // ...
        notifyObservers();
    }

    private void notifyObservers()
    {
        // notify observers
    }
}

Here are three cases I have in my code:

  • An observer is registered at program start and is never unregistered. In this case, all of the 3 options would be possible.
  • An observer is registered somewhere and needs to be unregistered when some external event occurs. The observer does not know about this external event and obviously it must be registered externally (option 1).
  • An observer is registered somewhere and needs to be unregistered when some external event occurs. The observer knows what happened, because it is also an observer of this external event. In this case, all of the 3 options would be possible.

In the case where all 3 options are possible, which one is the best from an OO and clean-code point of view?

Here is a list of some pros and cons I think each option has.

1. External registration

Pros:
- Less parameters in the constructor of the observer.
- Subject does not need to be abstracted to promote loose coupling.

Cons:
- One must not forget to register the observer in the client code.
- Client code is responsible for registration.

Neutral:
- The observer as an additional public method.
- Observer can be registered / unregistered by client code.

2. Internal registration, option 1: Concrete observer implements the Observer interface

Pros:
- Observer is responsible for registration.
- Registration cannot be forgotten, because one is forced to pass the Subject to the Observer's constructor.

Cons:
- One more parameter in the constructor of the observer.

Neutral:
- The observer as an additional public method.
- Observer can be registered / unregistered by client code.
- Observer can register / unregister itself.

3. Internal registration, option 2: Concrete observer does NOT implement the Observer interface

Pros:
- Observer is responsible for registration.
- Registration cannot be forgotten, because one is forced to pass the Subject to the Observer's constructor.
- Observer does not have and additional public method that could be abused by anything not related to "Subject notifying Observer".

Cons:
- One more parameter in the constructor of the observer.

Neutral:
- Observer can only register / unregister itself.

Aucun commentaire:

Enregistrer un commentaire