Observer Pattern

« Back to Glossary Index

Description


The Observer pattern (commonly referred to as Publish-Subscribe in some forms of the pattern) defines a one-to-many dependency between modules (or objects) so that when one modules changes state, all its dependents are notified and updated automatically. This is done without having the primary module know which dependents will receive the notification.

Warning

There is a debate over whether or not Observer and Publish-Subscribe are equivalent patterns. The canonical sources we have group these patterns together, and for a number of reasons we agree with this choice. For further discussion on our rationale as well as commonly cited differences, see Differentiating Observer and Publish-Subscribe Patterns.

Table of Contents:

  1. Aliases
  2. Context
  3. Problem
  4. Forces
  5. Solution
  6. Consequences
  7. Implementation Notes
  8. Implementation Examples
  9. Known Uses
  10. Variants
  11. Related Patterns
  12. References

Aliases

  • Publish-Subscribe
  • Event Listener
  • Dependents

Context

The Observer pattern is useful in the following situations:

  • When an object should be able to notify other objects without making assumptions about what the dependent objects are. In other words, you don’t want these objects tightly coupled.
  • When a change to one object requires changing others, and you don’t know how many objects need to be changed.
  • When an abstraction has two aspects, one dependent on the other. Encapsulating these aspects in separate objects lets you vary and reuse them independent

The Observer pattern is often used to implement the Model-View-Controller pattern. Callback functions are often extended via the use of the Observer pattern.

Problem

We build complex applications by having modules coordinate and communicate with each other. A common requirement is to have modules stay up-to-date with events that have occurred in another module. Additionally, we may have module relationships that are open-ended, where one module publishes information that may be of interest to any number of other modules. However, having modules directly interact with one another (e.g., invoking APIs) introduces tight coupling between them, which reduces reusability, testability, and ensures that changes in one module cascade to other modules. Wouldn’t it be better to find a way for modules to communicate and stay up-to-date without the introduction of tight coupling?

Forces

The Observer pattern balances the need to maintain communication dependencies between modules with the coupling between modules. Relationships between modules are kept open-ended rather than hard-coded within individual modules.

Solution

The Observer pattern divides modules into two relationship categories: Subjects and Observers, also called Publishers and Subscribers. Subjects publish notifications, and Observers can subscribe to receive notifications. Subjects provide an interface (either directly, or through a central broker) that Observers can use to subscribe to notifications. This way, modules that publish information do not need to be modified whenever a new subscriber is needed.

Note

This pattern is an example of the Dependency Inversion Principle.

Design Patterns: Elements of Reusable Object-Oriented Software presents the following model of the Observer pattern in its most basic form:


Structure of the Observer Pattern
Note

For the variation with a central broker, see the Centralized Broker section.

While the original pattern outline follows an object-oriented approach, but this is not actually a requirement to implement the pattern. You can make this a function-based paradigm, where you provide an API that allows registering function pointers (or other functors) rather than classes that adhere to the Observer interface. When the equivalent of a Notify operation is called, you can invoke each of the registered functions directly. In our opinion, this provides much more flexibility than enforcing a purely class-based pattern.

The different components and their collaborations are defined as follows:

  • Subject
    • Maintains a list of subscribed observers. Any number of Observer objects may observe a Subject.
    • Provides an interface for attaching and detaching Observer objects.
    • It is always a good idea to document which Subject operations trigger notifications.
  • Observer
    • Defines an updating interface for objects that should be notified of changes in a Subject.
  • ConcreteSubject
    • stores state of interest to ConcreteObserver objects.
    • sends a notification to its observers when its state changes.
    • ConcreteSubject notifies its observers whenever a change occurs that could make its observers’ state inconsistent with its own.
  • ConcreteObserver
    • maintains a reference to a ConcreteSubject object.
    • stores state that should stay consistent with the Subject’s.
    • implements the Observer updating interface to keep its state consistent with the Subject’s.
    • After being informed of a change in the concrete subject, a ConcreteObserver object may query the subject for information. ConcreteObserver uses this information to reconcile its state with that of the subject.

The following interaction diagram from Design Patterns: Elements of Reusable Object-Oriented Software illustrates the collaborations between a subject and two observers. Note how the Observer object that initiates the change request postpones its update until it gets a notification from the subject. Notify is not always called by the subject. It can be called by an Observer or by another kind of object entirely.



Note

This is the simplest form of the pattern, and we discuss many improvements and modifications in the Implementation Notes.

Consequences

  • The Subjects and Observers are only abstractly coupled together, because all the Subject knows is that it has a list of registered observers, each conforming to a specified interface (whether a function or a class). You can vary subjects and observers independently. Observers can be added without modifying the Subject module or other Observers.
    • This is often useful when communicating across layers in a system. Lower-level subjects can communicate with higher-level observers through the pattern’s mechanisms without violating the layering rules.
  • The Observer pattern enables broadcast communication and allows interested subscribers to opt-in to notifications.
  • Subscriptions to notifications eliminates the need to perform polling to see when state has changed. Subscribers only need to take action when a relevant event has occurred.
  • Changes in Subject modules related to published data and subscription interfaces may trigger changes in Observer modules.
  • There cannot be dangling references to deleted Observers, so de-registration and deletion must be properly managed together.
  • Subject state must be self-consistent before notifications are sent, since Observers may query the subject for its current state.

    You can avoid this pitfall by sending notifications from template methods (Template Method (325)) in abstract Subject classes. Define a primitive operation for subclasses to override, and make Notify the last operation in the template method, which will ensure that the object is self-consistent when subclasses override Subject operations.

Implementation Notes

When implementing this pattern, you can modify the basic implementation in the following ways:

  1. Identifying the Subject
  2. Push vs Pull Models
  3. Synchronous vs Asynchronous Notifications
  4. Topics vs Pointers
  5. Centralized Broker
  6. Delivery Guarantees
  7. Observers Must Not Change the Subject

Identifying the Subject

In a unified interface such as the basic model described above, it can be useful to reference the Subject in the notification so that Observers that depend on multiple Subjects can identify which Subject to examine. However, registering functions instead of classes works around this problem since different functions can be registered with different subjects, making it easy to see which triggered the notification.

Push vs Pull Models

The Observer pattern can essentially be implemented with two fundamental models: push and pull.

The basic pattern described above is the pull model, where the Subject only sends a minimal notification that a change occurred. Observers must ask the Subject for the relevant details explicitly once the notification is received. This model does not assume anything about the needs of registered Observers, and trusts that each object can update its state according to its requirements. However, this model also means that the Observers must ascertain exactly what changed on their own (unless fine-grained notification options are provided by the Subject).

The alternative to this is the push model, where the Subject sends Observers detailed information about the change along with the notification, whether that is the trigger for the notification or the latest available data.

Another consideration to make between the two models is the cost of the data: for example, if multiple change notifications are sent out asynchronously, the push model will require more memory to store each message in the queue since the data is embedded. However, the receiving module will get the full history of changes, which may be important for some applications. In the pull model, notifications are smaller, but only the latest data will be available in via the Subscriber interface regardless of the number of notifications received..

Synchronous vs Asynchronous Notifications

Some people claim that the Observer pattern requires synchronous notifications: The Subject invokes registered Observers directly. However, this is not a requirement for the pattern. Notifications can also be delivered in an asynchronous manner, such as by sending events to an event queue, sending a message to a message queue (or other messaging system), or adding functions to an asynchronous dispatch queue.

Topics vs Pointers

The basic example above uses pointers to classes, and we also pointed out that it will work equally well to pointers to functions (or another function storage mechanism). However, pointers are not the only way you can associate publishers and subscribers together. You can publish and subscribe to topics, which may be encoded as strings. You might also encode such topics as numerical identifiers. Finally, you might also specify some type of “content filter” that is applied to a generic message. These modifications are often associated with the use of a Centralized Broker, which is discussed below.

Another extension of this idea providing fine-grained control over what notifications are interesting to a given Observer. For example, a Subject may generate an event when a communication transaction has completed, whether successfully or in error. You may have an Observer that only needs to be updated when an error condition has occurred. In this case, you can provide another field during registration which allows an Observer to specify that they are only interested in error events.

Note

This is similar to the interrupt source configuration for processor peripheral modules, which allow you to specify which conditions will cause the processor to invoke the interrupt handler.

Centralized Broker

A popular enhancement on the basic pattern is the use of a centralized broker which manages subscriptions. For those that differentiate Observer and Publish-Subscribe, the use of a central broker is one of the key distinguishing features. Rather than relating Publishers and Subscribers to each other, both work through a central broker. Publishers register themselves and the topics they publish, and Subscribers register themselves along with the topics they are interested in. The central broker handles the routing between the two (and often manages other implementation details, like the order in which notifications are delivered and the mechanism of delivery, such as a message passing system).

Design Patterns: Elements of Reusable Object-Oriented Software provides the following representation of the pattern with a central broker, called the ChangeManager.

The following diagram depicts a simple ChangeManager-based implementation of the Observer pattern. There are two specialized ChangeManagers. SimpleChangeManager is naive in that it always updates all observers of each subject. In contrast, DAGChangeManager handles directed-acyclic graphs of dependencies between subjects and their observers. A DAGChangeManager is preferable to a SimpleChangeManager when an observer observes more than one subject. In that case, a change in two or more subjects might cause redundant updates. The DAGChangeManager ensures the observer receives just one update. SimpleChangeManager is fine when multiple updates aren’t an issue.

ChangeManager is an instance of the Mediator (273) pattern. In general there is only one ChangeManager, and it is known globally. The Singleton (127) pattern would be useful here.


Adjusting the Observer pattern for use with a centralized notification system (Publish-Subscribe style),

In the diagram above, ChangeManager has three responsibilities:

  1. It maps a subject to its observers and provides an interface to maintain this mapping. This eliminates the need for subjects to maintain references to their observers and vice versa.
  2. It defines a particular update strategy.
  3. It updates all dependent observers at the request of a subject.

Delivery Guarantees

It can be useful to manage notification deliveries in a more complex way, since there are different categories of notifications with different priorities:

  • Some modules have a higher priority than other modules and need to receive the information first
  • Time critical events need to be responded to within a certain maximum time
  • Some events are not critical in any way and can be handled when available

Observers Must Not Change the Subject

If more than one Observer is attached to a Subject, the following problematic scenario must be avoided:

  1. A Subject issues a notification to registered Observers.
  2. The first Observer receives the notification and makes a change to the Subject.
  3. The Subject then broadcasts a new notification for the updated state.

In a synchronous system this can be problematic: the second set of notifications will be processed, then (assuming no other state changes) the remainder first set of notifications will be processed (since the second set of changes was nested within the notification for the first set).

There are a few general solutions:

  • Do not let Observers modify subjects directly (via manual enforcement or API restriction)
  • Use a single event processing queue, which will preserve the ordering and ensure that all the outgoing notifications are processed before any new state change requests.
  • Use two queues: one for outgoing notifications and one for incoming events. Ensure that outgoing notifications are processed before incoming events.

Implementation Examples

Known Uses

  • In general, the Observer pattern is used whenever you want to provide a mechanism for some modules in a system to subscribe to notifications from another part of a system without tightly coupling them together. Variants of this theme include:
    • Enable modules to be notified of events raised by other modules
    • Subscribe to data that is published into the system
    • Manage callbacks
  • The Data Model Architecture, described in Patterns in the Machine : A Software Engineering Guide to Embedded Development and used in the corresponding source code, makes use of the Observer pattern within “Model Points” so that subscribers can be notified when new data is available. This use follows the polling pattern, where subscribers receive a notification and then access for the latest value contained within the model point through an API.
  • The best-known example of the Observer pattern appears in the Smalltalk Model/View/Controller (MVC) framework. The Model is the Subject, while the View is the base class for Observers.
  • Reactors register event handlers, and handlers are notified if a (relevant) event occurs.
  • MQTT is an open-source publish/subscribe messaging transport that is commonly used for connected embedded devices.
  • The Embedded Template Library provides an Observer template class that enable you to use the pattern in your programs. The ETL implementation requires that observer classes to use a define a notification API, and multiple overloads/types are supported. observable classes maintain a list of observers (with a specified maximum size), provide APIs for subscribing/unsubscribing, and notify users when new data is published. An example of this system is provided in the documentation.

Variants

Event Helix Documents two variants of this pattern:

  • Local Publish-Subscribe Pattern: Use this pattern when publisher and all the subscribers are a part of the same task.

  • Remote Publish-Subscribe Pattern: This pattern should be used when publisher and subscribers are implemented in different tasks/processors. All communication takes place via messages.

    [The RemoteStatusPublisher] class supports a message based interface. Subscribers send registration request message to register for the status change. The source address of the sender is saved as a part of the registration process. De-registration request is sent to stop receiving the status change notifications.

    Whenever status change is detected, PublishStatus method is invoked. This method sends the status change message to all the registered subscribers using the address that was obtained during registration.

  • Callback functions in their primitive form are a simplified version of the Observer pattern. The Observer pattern can be used to manage callbacks. Some distinguish the two patterns in the following ways:
    • Callbacks notify a single caller that an operation is completed (often, with the results), while Observer is often generalized to an event occurred (which may include a completion event).
    • Sometimes a callback is distinguished from an Observer because the notification is sent to the module that initiated the action, while Observer notifies any interested party, not just the one that triggered the operation.
  • The Observer Pattern appears as part of other patterns, such as Model-View-Controller.
  • There is a debate over whether or not Observer and Publish-Subscribe are equivalent patterns. The canonical sources we have group these patterns together, and for a number of reasons we agree with this choice. For further discussion on our rationale as well as commonly cited differences, see Differentiating Observer and Publish-Subscribe Patterns
  • For patterns related to the centralized broker variant:
    • Mediator – By encapsulating complex update semantics, the ChangeManager acts as mediator between subjects and observers.
    • The ChangeManager can be represented as a Singleton

References

  • Design Patterns: Elements of Reusable Object-Oriented Software by Gamma et al.

  • Differentiating Observer and Publish-Subscribe Patterns

  • C2 Wiki: ObserverPattern

  • C2 Wiki: Dependency Inversion Principle

    The ObserverPattern can be considered a two-layered architecture, where the model is the lower-level module and the higher level module is the observer.

  • C2 Wiki: Observer Must Not Change Observable

  • The Observer Pattern – ModernesCpp.com

    Use Case

    • One abstraction depends on the state of another abstraction
    • A change to one object implies a change to another object
    • Objects should be notified of state changes of another object without being tightly coupled
  • Observer pattern – Wikipedia

    The Observer pattern addresses the following problems:2(https://en.wikipedia.org/wiki/Observer_pattern#cite_note-2)

    • A one-to-many dependency between objects should be defined without making the objects tightly coupled.
    • It should be ensured that when one object changes state, an open-ended number of dependent objects are updated automatically.
    • It should be possible that one object can notify an open-ended number of other objects.

    Defining a one-to-many dependency between objects by defining one object (subject) that updates the state of dependent objects directly is inflexible because it couples the subject to particular dependent objects. Still, it can make sense from a performance point of view or if the object implementation is tightly coupled (think of low-level kernel structures that execute thousands of times a second). Tightly coupled objects can be hard to implement in some scenarios, and hard to reuse because they refer to and know about (and how to update) many different objects with different interfaces. In other scenarios, tightly coupled objects can be a better option since the compiler will be able to detect errors at compile-time and optimize the code at the CPU instruction level.

  • Publish–subscribe pattern – Wikipedia

    In software architecture, publish–subscribe is a messaging pattern where senders of messages, called publishers, do not program the messages to be sent directly to specific receivers, called subscribers, but instead categorize published messages into classes without knowledge of which subscribers, if any, there may be. Similarly, subscribers express interest in one or more classes and only receive messages that are of interest, without knowledge of which publishers, if any, there are.

  • Making Embedded Systems: Design Patterns for Great Software by Elecia White

    With the scheduler, we’ve built what is known as a publish/subscribe pattern (also called an observer pattern or pub/sub model). The scheduler publishes the amount of time that has passed, and several tasks subscribe to that information (at varying intervals). This pattern can be even more flexible, often publishing several different kinds of information.

    The name of the pattern comes from newspapers, probably the easiest way to remember it. One part of your code publishes information, and other parts of your code subscribe to it. Sometimes the subscribers request only a subset of the information (like getting the Sunday edition only). The publisher is only loosely coupled to the subscribers. It doesn’t need to know about the individual subscribers; it just sends the information in a generic method.

    Our scheduler has only one type of data (how much time has passed), but the publish/subscribe pattern is even more powerful when you have multiple types of data. This pattern is particularly useful for message passing, allowing parts of your system to receive messages they are interested in but not others. When you have one object with access to information that many others want to know about, consider the publish/subscribe pattern as a good solution.

  • Event Helix: Publish-Subscribe Design Pattern

    While developing embedded system, one frequently encounters a situation where many entities are interested in occurrence of a particular event. This introduces a strong coupling between the publisher and subscriber of this event change notification. Thus whenever a new entity needs the information, code for the publisher of the information also needs to be modified to accommodate the new request.

    The Publish-Subscribe Pattern solves the tight coupling problem. Here the coupling is removed by the publisher of information supporting a generic interface for subscribers. Entities interested in the information subscribe to the publisher by registering for the information. With this interface, the publisher code does not need to be modified every time a subscriber is introduced.

    Whenever information needs to be published, the publisher invokes the Publish method to inform all the subscribers.

  • Getting started with publish-subscribe messaging systems – Embedded.com

    Publish-subscribe is a messaging facility. It describes a particular form of communication between software modules or components.  The name is chosen to reflect the most significant characteristics of this communication paradigm.

    The Central Ideas of Publish-Subscribe

    • Software components do not necessarily know who they are communicating with.
    • Producers of data publish that data to the system as a whole.
    • Consumers of data subscribe to and receive data from the system as a whole.
    • Information is labelled so that software modules can identify the available information. This label is often referred to as the topic.
  • Observer · Design Patterns Revisited · Game Programming Patterns

    That’s what the observer pattern is for. It lets one piece of code announce that something interesting happened without actually caring who receives the notification.

  • DavidHoulding, DrDobbsJournal July 2000_ pg 88

    Publish and Subscribe is a well-established communications paradigm that allows any number of publishers to communicate with any number of subscribers asynchronously and anonymously via an event channel.

Synonyms:
Publish-Subscribe Pattern, Pub/sub
Categories: Field Atlas


« Back to Glossary Index

Share Your Thoughts

This site uses Akismet to reduce spam. Learn how your comment data is processed.