2

While working on a refactoring task of a legacy app, I came across a scenario where a series of cause->effect type events are happening and currently everything is being handled inline.

To give you an idea, a user uploads a file, then it is converted into a number of format, and on completion/failure of each format conversion task, an email is sent to some predefined addressees and on email delivery success, it is also logged to some file.

On first glance it seems like a typical use case of an observer pattern or pub-sub but I have came across this post and the linked paper and it got me thinking if I am going to end up into a mess for which I might not be ready.

I have been kind of living under a rock for quite a while now when it comes to the latest changes in software industry. If observer is not (anymore) the right or proper way to decouple the problems belonging to the type mentioned above, then has the industry (by industry I assume the mainstream of software engineers) already come up with an alternate that I don't know of?

  • 1
    For GUI work, data binding and reactive programming have subsumed manual observer implementations. Observers are great, but quite tricky to implement correctly and efficiently. E.g. using observers are one of the easiest way to create memory leaks in Java. – amon Jul 22 '19 at 7:36
4

Observer pattern is fine for smaller programs. As you mentioned, it properly decouples the event from the class which acts on that event.

However, wiring it up can get a bit complex, and although the event itself is decoupled, wiring up the event still requires that at least at some point, the observer must know about the subject.

So generally the simplest approach to this type of problem involves an event bus or message queue. Rather than A listens to B and B listens to C, and C listens to D type deal, you have a central event handling system whereby you can both register for events and listen to events. So A registers for event emitted by B, and B registers for event emitted by C, etc. None of the classes ever have to know the others exist, allowing you to completely decouple even during the registration of event listeners.

There is also an added advantage that by centralizing event handling, you can dedicate a thread towards dealing with it, allowing you potentially to both generate events and handle them at the same time. You would call this a message queue for the most part, as its main job is this.

You can take this a step further and serialize events as they arrive to the queue, so should the program fail before the event is executed, you probably wouldn't lose any progress, and you're sure that the events happen just the same.

Let me emphasize, observer pattern still works, but it isn't a complete decoupling, which is fine for small projects, but you should consider an event bus or message queue for larger programs.

  • Thanks Neil, really helpful. Now I am onto google to see if I can find some simple code samples to help me get started with this event bus thing. I guess as I already know how to implement observer, adding an extra layer to decouple the registration shouldn't be that hard. – lahory Jul 22 '19 at 6:57
  • @lahory Glad to hear it. Good luck to you! – Neil Jul 22 '19 at 7:10
  • Dependency Injection does registration just fine. – candied_orange Jul 22 '19 at 22:41
  • @candied_orange That's another solution, sure. However DI still requires that order is respected, and sometimes you get conflicts regarding ordering of the creation of certain objects. This problem doesn't come up if you're using an event bus or message queue. Instances can register to events in their own time, provided of course that it is done prior to any events being produced. – Neil Jul 23 '19 at 6:30
  • @Neil event buses and message queues also require things to exist before they are used. The observer pattern lets you register observers anytime after subject creation just fine. This is done with what DI calls setter injection. You just pass a reference. All you need is an injecter that knows about both of them. That could simply be main(). Observers actually don't have to know a thing about the subject. They just have to accept messages from them. – candied_orange Jul 23 '19 at 11:05
6

Quite the opposite, really.

Observer pattern has been so useful some languages and frameworks embraced it as first-class primitive. Events in C# being prime example.

Observer pattern can also be found on architectural level, where services can subscribe to events produced by other services.

But the real question is if observer and pub-sub patterns are actually good solutions to a problem. They are really good when there is clear separation between one that produces the even and the one that consumes it. Which is actually quite rare. I would think that that the example you describe is not really good place to use an observer. As the whole workflow seems to be tightly coupled.

And in architectural level, it seems to be just better to broadcast all events and have the consumer cherry-pick the ones that interest it instead of only sending to registered subscribers.

  • The point of registering is that subject doesn't have to know who to broadcast events to until told. – candied_orange Jul 22 '19 at 22:52

Not the answer you're looking for? Browse other questions tagged or ask your own question.