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:
In the cases 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 between Subject and Observer.
Cons:
- One must not forget to register the observer in the client code.
- Client code is responsible for registration.
Neutral:
- The observer has 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 has 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 an 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.
Given the subtle differences you raised between 'external' and 'internal' registrations, it looks like there won't be one right answer. Still, I'll try.
I'd prefer 'external' registration to other two for two reasons:
Observer
s don't know anything about the Subject
s; i.e., they are fairly decoupled. For e.g., I can attach one Observer
to multiple Subject
s, and no one would have to change.Observer
only cares about what it needs to do when inform
ed. It doesn't care about registering/unregistering with anyone.If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With