Pattern Observer and Spring

Viewed 614

I use Spring. I implemented the pattern Observer as shown in the examples on the Internet.

@Component
public class ObserverManager {

    private Map<String, List<IObserver>> observers = new HashMap<>();

    public void subscribe(String type, IObserver observer){
        // do something
    }

    public void notifyObserver(String type, Object data){
        observers.get(type).forEach(observer -> observer.update(data));
    }

    // more
}
@Component
public class NewsObserver implements IObserver {
    @Override
    public void update(Object o) {
        //do something
    }
}

But I don't understand how to correctly add an observer to ObserverManager. It can be injected via the constructor. But if the observers are 7-8? This is a lot for the constructor.How to solve this problem correctly?

1 Answers

There are many approaches to this problem, which include (could be more):

Observers registering themselves

A very common approach to this problem is to have the observers register themselves, this is clean as an observer knows exactly what it wants to observe.

@Component
public class NewsObserver implements IObserver {
    @Autowired
    public NewsObserver(IObserverManager observerManager) {
        observerManager.subscribe("interestingType", this);
    }

    @Override
    public void update(Object o) {
        //do something
    }
}

Inject a collection

Another approach would be to use a List<IObserver> in the ObserverManager constructor:

@Autowired
public ObserverManager(List<IObserver> observers) {
    observersByType.put(type, observers);
}

But where this type comes from? If it's the observer that knows about it, you would have to first iterate the received list and get the proper type from the observer and register each one of them with that type.

Using configuration Beans

By dropping the @Component from the NewsObserver and registering the beans manually, you can do something like:

@Bean
@Autowired
public IObserver newsObserver1(ObserverManager manager) {
    IObserver o1 = new NewsObserver();
    manager.subscribe("type1", o1);
    return o1;
}

@Bean
@Autowired
public IObserver newsObserver2(ObserverManager manager) {
    IObserver o2 = new NewsObserver();
    manager.subscribe("type2", o2);
    return o2;
}

What to choose?

I guess it depends.

IMO, the cleaner approach is the first. Notice how easy is to inject a mocked ObserverManager for testing. And who better to know what type of news the observer is interested in than the observer itself? The observers can control what and when they are observing and when to stop it.

EDIT: As correctly pointed out in the comments, this introduces a cyclic dependency between the observer and the observer manager. As it stands, because the instance of observer manager is not kept in the observer and there's no way you can call the observer constructor twice for the same observer instance, the loop won't ever happen. But it's still a design smell that may lead to problems in the future.

Cyclic dependencies between two entities are generally solved by introducing a third-party to manage the relationship between those two entities, which leads us to one of the mentioned alternatives.

The configuration beans approach is interesting, in the sense that there's no dependency in the NewsObserver, hence, no cyclic dependency, it just sits there's and waits for updates. It should be noted though, that managing which observer receives which type and when to stop the subscription needs to be done outside the observer, which may not seem natural.

Notes:

As a side note, consider using a Multimap (available for example with Guava) for the observers map. It's easier to manage than a Map of List, but it requires an extra dependency because Java has no built-in type like it.

Also, unless you really want, there's no need to implement the Observer pattern yourself. These are really robust implementations of it and offer really good operations on top of it:

Related