DEV Community

Victor Miene
Victor Miene

Posted on • Updated on

Understanding the observer pattern

What are design patterns

Design patterns are standard template solutions to recurring problems in software engineering. You can think of it as a blueprint you can use to implement your own solution. The observer pattern is an object-oriented pattern that establishes a one-to-many dependency between two types of objects. These objects are usually called observable and observer.
Imagine a chat application where only an admin gets to send messages to all the users in the application. Messages being sent on the chat are very important and users would like to know immediately a new message is sent. In this modern day, the solution to this design problem is the intuitive notification feature we are accustomed to. Aside notifications, there are other ways users can get new messages from the application. As a software developer, how would you design this system?

Pushing vs Polling

A way to do this is for users of the application to keep making requests for new messages, then the system in turn would send the message across if one is available. The problem of designing the system this way is that we do not know the exact time a new message will be sent, and users may end up making multiple requests before getting a new message. It is quick to see that this implementation is not an ideal solution.
A more efficient way to do this is obviously notifications, and the observer pattern implements this solution.
In the observer pattern, the chat application would simply just send users the message when the admin creates a new message. The entity where the change occurs, in our case, the chat application is called the Observable and the users who wants to know about the message are the Observers or subscribers. In observer pattern terms, we would say observers subscribe to an observable, and the subscribers in turn would be notified when there is a change of state. The relationship shared between the observable and its subscribers is a one-to-many relation which just implies that an observable can have one or more subscribers but note that this relationship does not go the other way around. An observer can only listen for changes in a unique observable.

How would you do implement this pattern?

NB: This implementation was done using JavaScript.
Step 1: creating the observer
The observer is a class that implements a method called update which is used to react when a change occurs in the observable.

class User {
  constructor(username, chatApi) {
    this.username = username;
    this.chatApi = chatApi;
  }

  /* A bunch of other application logic. lol*/

  //used by the Observer to notify users when a new message has been added
  getNewMessage(message) {
    console.log(`New Message: ${message}`);
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 2: creating the observable
The observable is defined as a class that does all the following:
• Stores a list of all its subscribers.
• Exposes method that can be used to subscribe or unsubscribe an observer
• Implement a notify method which is called when there is a change of state in the observable.

class ChatApi {
  constructor() {
    this.users = {};
  }

  //returns true if the user was registered (Subscribe)
  registerUser(user) {
    const { username } = user;
    if (!this.isUserAvailable(username)) {
      this.users[username] = user;
      return true;
    }
    return false;
  }

  //returns true if the user was unregistered (unsubscribe)
  unregisterUser(user) {
    const { username } = user;
    if (this.isUserAvailable(username)) {
      delete this.users[username];
      return true;
    }
    return false;
  }

  notify(message) {
    Object.values(this.users).forEach((user) => {
      user.getNewMessage(message);
    });
  }

  addNewMessage(message) {
    /* Logic for sending message */
    this.notify(message);
  }

  //confirms that a user exists
  isUserAvailable(username) {
    if (this.users.hasOwnProperty(username)) {
      return true;
    }
    return false;
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Integrating your own business logic
Apart from the subscriber and observable mechanisms we have discussed about your application might need to do other things. In our chat application example, both objects will need to be able to send and receive messages.

An example of how you would use this is below:

const yourChatApi = new ChatApi();
const userOne = new User("douyeszn", yourChatApi);
const userTwo = new User("moonboy", yourChatApi);
yourChatApi.registerUser(userOne);
yourChatApi.registerUser(userTwo);
yourChatApi.addNewMessage("Listened to Donda yet?");
Enter fullscreen mode Exit fullscreen mode

Top comments (0)