The article you’re about to read is part of a bigger series about software design patterns implemented on languages using the JVM, mostly Java. In this one the Observer pattern is presented. Please notice this article has nothing to do with Reacting Programming or Rx.
Table of contents
Description
Gang of Four definition: Define a one-to-many relationship between objects so that when one object changes its state, all its dependents are notified and updated automatically.
As reactive programming uses similar concepts, we will borrow some terms from Rx to explain the concept.
In this pattern we can identify two components:
- Observable: this subject emmits some data and keeps a list of observers interested in receive the data he emmits. Originally the Observable was called Subject.
- Observer: the observer register themselves to the observable indicating he is interested in receiving any updated. When he looses interest of receiving new data, he unsubscribes from the Observable.
Some times, this pattern is also referred to as Publisher - Subscriber model. As a real life example, we can take Twitter: there is interesting people from which you want to get latest updates, so you follow them and each time they publish something, you will receive it in your timeline feed.
Use cases
This pattern results very useful when you switch from a pull to a push data retrieval paradigm. This way you don’t need to query your data source periodically to get new data, when new data is inserted into the data store, you will be notified. Imagine a chat, we can periodically check for new messages on a conversation, but that won’t be efficient. Better open an channel and receive notifications any time a nice message was published into some conversation.
Pros
- When Observable dataset changes, all Observers are notified. Effective data sending.
- Can stop receiving updates any time by unregistering from the Observable.
- It supports the principle of loose coupling between objects that interact with each other.
Cons
- As we are extending Java’s built in
Observable
class, we cannot use Composition. - The order of Observer notification is undependable. Not applicable to our example, as we use Java’s Observable, and notifications are sent in order of subscriptions.
Implementation
A very basic test specification for this patterns can be the following tests:
public class NetworkStateObservableShould {
@Test
public void register_observers() throws Exception {
NetworkStateObserver observer = anyObserver();
networkStateObservable.addObserver(observer);
assertThat(networkStateObservable.countObservers(), equalTo(1));
}
@Test
public void unregister_observers() throws Exception {
int NO_OBSERVERS = 0;
NetworkStateObserver anyObserver = anyObserver();
networkStateObservable.addObserver(anyObserver);
networkStateObservable.deleteObserver(anyObserver);
assertThat(networkStateObservable.countObservers(), equalTo(NO_OBSERVERS));
}
@Test
public void send_updates_to_observers() throws Exception {
givenMockedNetworkState();
NetworkStateObserver mockedObserver = mockedObserver();
networkStateObservable.addObserver(mockedObserver);
networkStateObservable.startObserving();
verify(mockedObserver).update(networkStateObservable, EXCELENT);
}
@Test
public void dont_send_updates_when_no_one_is_observing() throws Exception {
NetworkStateObservable mockedNetworkStateObservable = mock(NetworkStateObservable.class);
mockedNetworkStateObservable.startObserving();
verify(mockedNetworkStateObservable, never()).notifyObservers();
}
}
Some methods were skipped to keep the article slim but can be found at Design Patterns repository.
As you can see, there are some classes that need explanation. In this example we are working as mobile developers and need to receive
notifications any time mobile’s network quality changes. The artifact who sends us all network changes is NetworkStateEmitter
.
public interface NetworkStateEmitter {
enum NetworkState {
POOR,
STABLE,
EXCELENT
}
@FunctionalInterface interface NetworkStateListener {
void onNetworkStateChanged(RandomNetworkStateEmitter.NetworkState state);
}
void receive(NetworkStateListener listener);
}
And our observable resource is NetworkStateObservable
, this time we’re extending Java’s built in java.util.Observable
class.
public class NetworkStateObservable extends Observable implements NetworkStateListener {
private NetworkStateEmitter networkStateEmitter;
public NetworkStateObservable(NetworkStateEmitter networkStateEmitter) {
this.networkStateEmitter = networkStateEmitter;
}
public void startObserving() {
this.networkStateEmitter.receive(this);
}
@Override public void onNetworkStateChanged(NetworkState state) {
super.setChanged();
super.notifyObservers(state);
}
}
Java’s built in Observable
class already provides us methods to handle operations over the Observable, like: addObserver
, deleteObserver
, notifyObservers
, notifyObservers
, deleteObservers
, setChanged
, clearChanged
, hasChanged
, countObservers
.
For the Observer, the class who receives updates from Observable, we also use a Java built in interface java.util.Observer
:
public class NetworkStateObserver implements Observer {
private final int observerNumber;
public NetworkStateObserver(int observerNumber) {
this.observerNumber = observerNumber;
}
@Override public void update(Observable observable, Object state) {
NetworkState networkState = (NetworkState) state;
System.out.println(String.format("Observer %d: %s", observerNumber, networkState));
}
}
Above classes ands tests are enough for a simple Observable pattern implementation and ussage, but in the repository I also left a basic working example with a RandomNetworkStateEmitter
which emmits a random NetworkState
every two seconds and results are printed out from the Observers.
NetworkStateObserver firstObserver = new NetworkStateObserver(1);
NetworkStateObserver secondObserver = new NetworkStateObserver(2);
NetworkStateObserver thirdObserver = new NetworkStateObserver(3);
RandomNetworkStateEmitter networkStateEmitter = new RandomNetworkStateEmitter();
NetworkStateObservable networkStateObservable = new NetworkStateObservable(networkStateEmitter);
networkStateObservable.addObserver(firstObserver);
networkStateObservable.startObserving();
networkStateObservable.addObserver(secondObserver);
networkStateObservable.addObserver(thirdObserver);
All the code from this post can be downloaded from the Design Patterns repository.