Over a million developers have joined DZone.

Design Patterns: Observer Pattern

The good, the bad, and the ugly truth about using the observer pattern in Java.

· Java Zone

What every Java engineer should know about microservices: Reactive Microservices Architecture.  Brought to you in partnership with Lightbend.

It's a little difficult to understand why the observer pattern is needed in the first place, and it comes down to the basic fundamental requirement of OO systems, which is to pass a message from one object to another telling the object to do something as a result of a state change on another object; but the big difference here vs. simply invoking a method on a dependent object is that you care about decoupling one way or another. Perhaps you have an event for which you want to trigger observers, multiple observers, or multiple endpoints that react to a particular endpoint because you want some loose coupling to want to change which observer actually gets triggered at runtime. This is the original reason you need the observer pattern.

The way you implement the observer pattern is sort of antithetical to the original goal of the observer. The GoF way is to implement the interface on a bunch of objects, then on the listener you have to register the concrete implementations of each of those instances.  It’s a bit self-defeating if the goal is to create loosely coupled systems.

How does Java EE solve this? It does so in a more loosely coupled fashion:

public class PublishService {

 Event<String> event;

 public void producer(){
 event.fire("Take me to your leader");



In this code snippet we have a stateless bean that is listening for events of type String. When it hears such an event it calls event.fire and gives it the payload that the observer is expecting. In this case, it is a welcome message. All dependants are notified.

public class MessageObserver {

 public void trace(@Observes String message){


The observer has a method with its expected payload, which is marked with the Observe annotation. This says: observe for any events that match the payload type of the parameter, in this case a string.

You can also use qualifiers here to filter events. You can qualify the Observes annotation by using the Named annotation or a custom qualifier, when you fire it you can statically provide the qualifier at the event injection point and also in the observer method parameter.

Event<String> event;

public void trace(@Observes @WarningMessage String message){}

By default they are synchronous, however in CDI 2.2 they are introducing asynchronous processing of events.

Use Case

Image that you have significant events in your application that you need multiple endpoints to react upon and they are not important enough to put in messaging middle-ware perhaps a system warning: this light-weight approach is perfect for that situation.

You might be thinking: what about transactions? Events don’t have much to do with transactions, the only way that they interact is that you can have an observer that listens on the transaction so you can get call backs saying that the transaction got committed or rolled back.

The Good, Bad and the Ugly

The Good 

It is very easy to implement with no boilerplate code. It can be used as a light weight messaging system that is less than JMS, as the container does the heavy lifting.

The Bad

The execution order is confusing but an IDE will help.

The Ugly

Nothing, its beautiful.

Microservices for Java, explained. Revitalize your legacy systems (and your career) with Reactive Microservices Architecture, a free O'Reilly book. Brought to you in partnership with Lightbend.

design patterns,java

Published at DZone with permission of Alex Theedom. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}