Is my use of observer pattern in this js sample correct?

I’ve been trying to get my head around the observer pattern. Every time I read an article or see a video tutorial it seemed easy.

Can you check if what I have done is in fact the observer pattern? Here’s the code in codesandbox

I created a very basic shopping card thing with three classes: Store, Products, Cart - where the Store is the observer. I pass the Store to the other two and let them push/register observer methods, and also let them trigger them all.

In my head the Store should be publishing something and the other two classes should be listening, whereas what this code is doing is taking requests!

It’s like if you are a radio station DJ and I give you a CD and tell you “when I notify you, play the CD”

Whereas I thought it was the other way around. You, the Store DJ play whatever song, and I’ll start dancing at home if you happen to play my favorite song

If someone could glance at this basic codesandbox just to let me know if I got it right.

Are you just trying to learn the pattern? If so, a shopping cart is not the best way to wrap your head around it.

Assuming you are just wanting to learn the pattern, I would suggest you use something closer to a real life publisher/subscriber model such as a Newspaper or Magazine Distributor.

I will use my real life Sirius Radio subscription as an example. In my car radio I have set favorite songs/artists (I subscribed, I am the observer). When Sirius Radio (publisher) is going to play (publish) one of my “favorites” I am notified on the screen so I can change to the channel to hear it. Same thing happens for every other person that has subscribed to Sirius Radio and whose favorite songs/artists are about to play which may or may not be the same as mine.

All of us subscribers can also unsubscribe at any time, either from the favorites or the service completely.

As I said I completely understand that but in the actual code it seems as though the juggler is told to do something different for each observer

export default class Juggler {
  constructor() {
    this.name = "juggler";
    this.observers = [bobWantsX(), AliceWantsY(), MarkWantsZ()];
  }
  registerObserver(observer) {
    this.observers.push(observer);
  }
  notifyObservers() {
    this.observers.forEach((observer) => observer());
  }
}

As you can roughly see, Bob registered for X, Alice registered for Y, Mark registered for Z.

Whereas your explanation and everything else I have read, the juggler just juggles() and if Bob wants to see it he registers for it.

From everything I have seen I think my code must be correct just that I didn’t expect when notifyObservers runs it would run like that

Going back to your cart example, does it really make sense to pass a store to a cart? When you go to the grocery store do you pass the store to your buggy/shopping cart or do you pass your groceries to the store for checkout?

const observer = new Store(); 
const cart = new Cart({ observer });

It’s a bad name, but that’s the observer. From the Cart I register displayCartItems, I do the same in products where I register the showCartCount

It shouldn’t be called a store but an observer (like the variable name) and the cart does need access to its register method.

I would start with a generic implementation instead of a concrete one such as a shopping cart. This should make it easier for you to “get your head around” the pattern.

<script>
class Subject {
    constructor () {
        this.criticalNumber = 0
        this.observers = []
    }

    addObserver (observer) {
        this.observers.push(observer)
    }

    removeObserver (observer) {
        let index = this.observers.findIndex(o => o === observer)
        if (index !== -1) {
            this.observers.splice(index, 1)
        }
    }

    notify () {
        console.log('Notifying observers about some important information')
        this.observers.forEach (observer => {
            observer.update(this.criticalNumber)
        })
    }

    changeCriticalNumber () {
        /* Changing the critical information */
        this.criticalNumber = 42
        /* Notifying the observers about this change */
        this.notify()
    }
}

class Observer {
    constructor (id) {
        this.id = id
    }

    update (criticalNumber) {
        console.log(`Observer ${this.id} - Received an update from the subject ${criticalNumber}`)
    }
}


let s = new Subject()

s.addObserver(new Observer(1))
s.addObserver(new Observer(2))

/* Changing the critical information */
s.changeCriticalNumber()

</script>

1 Like

Not that it makes much of a difference, but for removing an observer you tend to see Array filter being used.

removeObserver (observer) {
  this.observers = this.observers.filter(o => o !== observer)
}