Learn the Observer Design Pattern
This is the 17th post in a series on design patterns.
Observer is a behavioral design pattern that lets you define a subscription mechanism to notify multiple objects about any events that happen to the object they are observing.
Assume you have two types of objects: Customers and Stores. A customer is extremely interested in a product that is currently out of stock. Customers could visit the store every day or call the store to check product availability. However, the product is still unavailable and most of these visits/calls would be futile.
Observer Design Pattern
The object that has some interesting state is often called a subject, but since it will notify other objects about the changes to its state, we will call it a publisher. Subscribers are objects that track changes to the publisher’s state.
Observers suggest adding a subscription mechanism to the publisher class so individual objects can subscribe to or unsubscribe from a stream of events coming from that publisher. The publisher will now notify its subscribers whenever an important event occurs by calling the notification methods of their objects.
UML Class Diagram
Not familiar with UML Class Diagram? I have written a detailed post on the UML Class diagram.
Implementation steps
- Your business logic should be broken down into two parts. The core functionality, independent of other code, should be a publisher, while the rest will become subscriber classes.
- Define the subscriber interface. It should at a bare minimum declare a single update method.
- Define the publisher interface and describe the methods for adding and removing subscriber objects. Publishers must interact with subscribers only through the subscriber interface.
- Decide where to put the actual subscription list and how to implement subscription methods. Because this code is usually the same for all types of publishers, the obvious place to put it is in the abstract class derived directly from the publisher interface. Concrete publishers extend this class, inheriting subscription functionality.
- Create concrete publishers. A publisher must notify all its subscribers whenever something important occurs within it.
- Create concrete subscriber classes that implement the update notification methods. The majority of subscribers will require some context information. It can be passed as an argument for the notification method.
- The client must create all necessary subscribers and register them with proper publishers.
Source Code Implementation
NumberSubscriber is the interface for notifying subscribers. It usually consists of a single update method. The method may have several parameters that enable the publisher to send some event details with the update.
NumberPublisher sends events that are of interest to other objects. These events occur when the publisher changes its state or performs some behaviors. Subscription infrastructure in Publisher allows new subscribers to join and existing subscribers to leave.
Whenever a new event occurs, the publisher goes over the subscription list and calls the notification method declared in the subscriber interface for each subscriber object.
HexNumberSubscriber and OctalNumberSubscriber perform some actions in response to a publisher’s notification. NumberPublisher can be passed as an argument, allowing subscribers to directly fetch any required data.
The ObserverClient creates subscriber and publisher objects separately and then registers subscribers for publisher updates.
// Output
Number generated : 23
Number in Hex format : 0x17
Number in octal format : 027
When To Apply Observer Design Pattern
- The Observer pattern is used when changing the state of one object may require changing other objects, and the set of objects is not known ahead of time or changes dynamically.
- When some objects in your application must observe others but only for a limited time or under specific circumstances, use this pattern.
Pros of Observer Design Pattern
- It is possible to add new subscriber classes without changing the publisher’s code.
- Relations between objects can be established at runtime.