DEV Community

Cover image for Observer Pattern: a quick peek
steviepee
steviepee

Posted on

Observer Pattern: a quick peek

If you're a person of the code, you've without a doubt encountered many
forms of software design. From constructor, to factory, to Prototype, to Module and Mediator, Memento.. Okay, maybe I'm getting carried away. The point is that there are so many different designer patterns to choose from. It's an ocean of potential code patterns, and here's a chance to dip your feet in, for just a bit.
Let's start by talking about just one pattern. The Observer Pattern

What is Observer Pattern

According to Wikipedia, the Observer Pattern is a behavioral pattern ... in which an object, named the subject, maintains a list of its dependents, called observers, and notifies them automatically of any state changes, usually by calling one of their methods.1 Think of it as if the subject is a Parent that can't wait to call up all its children when something cool happens. Or, to put it into more stern terms, the Observer Pattern ...lets you define a subscription mechanism to notify multiple objects about any events that happen to the object they’re observing.2

Within this pattern, the purpose of the observer is simply to take changed subject state information and inform all of its "subscribers". I say subscribers here, because the Observer pattern is only used when there is one-to-many relationship between objects2. Here's an example of how a Subject relates to its observers:

//let's start with the subject(observable)
let Subject = {
constructor() {
  this._state: 0, 
// ^^represents what the observers are... observing, and checking for changes
  this._observers: [], //An array to hold the list of subscribers to inform of changes
}
  // now we need a function that adds an observer to the list. Let's give it a simple push
  subscribe: (observer) => {
    this._observers.push(observer)
  },
  // and a function that takes a target observer out of the list
  unsubscribe: (targetObserver) => {
    this._observers.filter((sub) => {
      return sub !== targetObserver;
    })
  },
  // also, a function that returns the current state wherever's necessary
  getState: function() {
    return this._state;
  },
  setState: function() {
    this._observers.forEach((observer) => {
      observer.update(this) // We'll set signal as an observer method

    })
  }
};

// And now, the observer
let Observer = {
  update: (subject) => {
    let currentValue = subject.getState()
    console.log(currentValue)
  }
}
Enter fullscreen mode Exit fullscreen mode

How is Observer Pattern?

As was said earlier, the Observer Pattern defines a one-to-many relationship between a subject and its observers. Now that we know what it does, let's take a little look at just how that relationship is realized.

Two Paths diverge

There are essentially two ways the Subject and its Observers can communicate, or interact, with each other. In one model, the Observers query for and take in the information they seek. In the other, the Subject simply sends its specific bits of state information to each of its subscribers. Because of their modes of action, these models of interaction have simply been dubbed pull and push. We'll explore these models through one of the simplest info blocks I can think of: a coin toss.

Image description

Pull

  In a pull interaction model, Observers register themselves with the Subject as they are created. Whenever the Subject changes, it broadcasts to all registered Observers that it has changed, and each Observer queries the Subject for that subset of the Subject's state that it is responsible for monitoring.3

 let Subject = {
    constructor() {

      this._state = {
        heads: 0,
        tails: 0,
        edge: 0
      },
      this._observers = []
    },
/* here, getState is an object, holding each state value  
 individually, waiting for an observer to choose which value it needs */
    getState: {
     heads: function() {
      return this._state.heads;
},
tails: function() {
      return this._state.tails;
},
edge: function() {
      return this._state.edge;
}
}
    },
    setState: function() {
      this._observers.forEach((observer) => {
        observer.update(this);
      })
    }
  }

  let Observer = {
    constructor(result) {
      this[result] = 0
    },
// each observer will now use its update method to pull its designated value from the _state when informed
      update: (subject) => {
        let currentValue = subject.getState[result];
        this[result] = currentValue;
        return this[result];
      }

  }
}
const flipTally = new Subject;
const headCheck = new Observer(heads);
const tailCheck = new Observer(tails);
const edgeCheck = new Observer(edge);
Enter fullscreen mode Exit fullscreen mode

Image description

possible depiction of the pull method in action(picture by Refactoring Guru(4).)

Push

In the push interaction model, *The subjects send detailed information about the change to the observer whether it uses it or not. * In this case, the observers will essentially sift through the given information for the bit each one needs.

 let Subject = {
      constructor() {

        this._state = {
          heads: 0,
          tails: 0,
          edge: 0
        },
        this._observers = []
      },
       /* in this example, getState is a function that will return the entire _state object to each observer, no matter what they're on the look out for...*/
      getState: function() {
        return this._state;
      },
      setState: function() {
        this._observers.forEach((observer) => {
          observer.update(this);
        })
      }
    }

    let Observer = {
      constructor(result) {
        this[result] = 0,
        this.tempState = {}
      },
        // ^^set a temporary state to hold the info given by getState
        update: (subject) => {
          // here, the observer sets a variable to receive the _state object
          let currentValue = subject.getState();
          // then, place that value as its own tempState 
          this.tempState = currentValue;
          /*after that, an observer will take the value it is searching for
and return only that specified information*/
          this[result] = this.tempState[result];
          return this[result]
        }

    }

  const flipTally = new Subject;
  const headCheck = new Observer(heads);
  const tailCheck = new Observer(tails);
  const edgeCheck = new Observer(edge);
Enter fullscreen mode Exit fullscreen mode

Image description

Choose your destiny

The coding world certainly is fraught with tradeoffs. It is up to the programmer(you) to choose which model would work best for your needs. In the "pull" model, the observer makes a query after being notified by the subject, adding to complexity and providing a space for inaccuracy in cases where a new change can occur between these moments of communication... If changes are made in the subject state after the observer is notified, it will be refreshed with an old state.5. In the push model, the subject could possibly send mountains of unnecessary data to each observer upon each change, requiring more direct coupling for a more tailored passing of data, which makes using the model for observers that need different information more problematic. In short, the "push" model compromises reuse, while the "pull" model is less efficient.3 It's up to you to decide which path is better for your particular program.

Conclusion

Today, we've taken a closer look at one of many design patterns we use every day in our programs. And I must say, as I've said before, that the coding world is fraught with tradeoffs. There is always a decision to make about what is best for your system, and what can help your program excel at its intended purpose. There are so many more designs and so many more decisions to make, and this is the reason why our programs feel so alive and real, reacting to our every choice in measurable ways. The beauty of change in action, demonstrated here with a dip in the ever-changing waters of the code. Thanks for taking a dip with me. Maybe next time, we can make it a dive. Until then, happy coding.

1.https://en.wikipedia.org/wiki/Observer_pattern

2.https://www.tutorialspoint.com/design_pattern/observer_pattern.htm

3.https://sourcemaking.com/design_patterns/observer

4.https://refactoring.guru/design-patterns/observer

5.https://www.oodesign.com/observer-pattern/

Top comments (0)