level up your Object Oriented JS with behavioral patterns(object communication)

Sk - Sep 12 '21 - - Dev Community

Behavioral  patterns - facilitate object communication

object communication is a vital piece in OOP, encapsulating data and functionality is the first step, communication is the most crucial step, a program is made up of smaller programs which are objects, communication knits everything together to a full functional system

Observer pattern

 A subject(object) maintains a list of its dependents(observers) and notifies them when state changes;

its one to many relationship, single object being observed for changes by multiple objects


// pseudo 
  Object subject:
     list -> subscribers
     state -> {}


     subscribe(callback){
        add callback to list 
        return unsubscribe fn


     }

     notify(){
      loop over list 
      call every function

     }

     setValue(key, value){
     change state 
     notify subscribers with new state

     }





Enter fullscreen mode Exit fullscreen mode

list is an array of callback functions, called by notify() on state change

Implementation

class Subject{
  #subscribers = []   // # means private,
  #state = {} 


      subscribe(fn){
           // adding the callback to the subscribers array
          this.#subcribers.push(fn)



             // unsubscribe function, when called will filter the array for fn, and return 
             // array without fn, this is possible because its a closure function
             return () => {

              this.#subscribers = this.#subscribers.filter(sfn => sfn != fn)

             }



     }

        setValue(key, value){

             // metaprogramming for setting a property to an object 
             // same as Object[key] = val, 
             let result = Reflect.set(this.#state, key, value)


             // reflect returns true if set was a success
             if(result)

               this.#notify()  // notify all the subs of the change

             }

        }



     // looping over all subs and notifying them with the new state
     #notify(){

        this.#subscribers.forEach(sfn => sfn(this.#state))

     }




}





Enter fullscreen mode Exit fullscreen mode

Usage


 let subject = new Subject()




let sub1 = subject.subscribe((state)=> {

  console.log("sub1",state)

})




let sub2 = subject.subscribe((state)=> {

  console.log("sub2",state)

})




let sub3 = subject.subscribe((state)=> {

 console.log("sub3",state)

})



let time1 = setTimeout(() => {

     sub3()  // unsubscribing 

     sub2()

     clearTimeout(time1)

}, 4000);


// changing state 

let index = 0;

let int = setInterval(() => {

     subject.setValue("index", index)



     index++;

}, 3000);



Enter fullscreen mode Exit fullscreen mode

While powerful, Observer pattern is limited in what it can do, for example what if we need multiple objects to communicate with each other rather than a single object, we definitely cannot make them all subjects, that would be a nightmare to maintain and keep track off, that is where PubSub comes in

PubSub

PubSub object maintains a list of channels and subscribers to those channels, instead of observing an object, channels are observed and any change to a channel notifies the subscribers, the interesting part is PubSub does not manage the change an object outside PubSub notifies PubSub with the change on a specific channel and all objects listening on that channel will be notified


 // pseudo

 class PubSub{
    subscribers = {channel : [list of subscribers]}

    subscribe(channel, callback){
       if channel does not exist:
         create a new channel and push the callback
       else push the callback to channel


       return unsubscribe function

       }

       // destructuring data(arguments) into an array to handle arguments of any size 
       // this way pubsub is object agnostic, can work with an number of arguments
     notify(channel, ...data){
        if(channel exist)
          notify every subscriber giving it the data

     }
 }



Enter fullscreen mode Exit fullscreen mode

Implementation

class PubSub{



     #subscribers = {}





     subscribe(channel,fn){

         if(!this.#subscribers[channel]) {

            this.#subscribers[channel] = []

         }

           this.#subscribers[channel].push(fn)




         return () => {

            this.#subscribers[channel] = this.#subscribers[channel].filter(sfn => sfn != fn)

         }




     }



         notify(channel, ...data){

             if(this.#subscribers[channel]){

                 this.#subscribers[channel].forEach(fn => {

                 fn(...data)

             })

          }

        }

}





Enter fullscreen mode Exit fullscreen mode

Usage


 const pub = new PubSub()

 let unsub1 = pub.subscribe("Hodo", (...data)=> {  // subbing to channel hodo
                                                   // spreading arguments into an array, to handle any number of args

     console.log(data)

 })

 let unsub2 = pub.subscribe("Danny", (...data)=> {

     console.log(data)

  })


    setInterval(() => {

     pub.notify("Hodo", "hodo", "hodo", "hodo")  // notifying channel Hodo with 3 arguments(...data)

     pub.notify("Danny", "Mother of Dragons") // single argument

   }, 2000);



Enter fullscreen mode Exit fullscreen mode

PubSub is very versatile and powerful as a start to object communication. now you should be able to knit any number of objects together and establish reactive communication

Thank you

Thank you for reading, if you found this useful or not feedback is greatly appreciated

If you want to level up your JavaScript, want to close that gap between beginner and advanced beginner quickly or you are a framework person and never had a chance to dive in JS, I am publishing an eBook soon JavaScript for advanced beginners which covers a range of topics from code completion, OOJS, iterators and generators, computational media, metaprogramming and more, and available for pre-order

or

You need a personal JS mentor, whether you are a beginner looking for a path, or you just want to level up your JS, or maybe you are stuck and seeing no direction or growth shoot me an email: mhlungusk@gmail.com or better yet a DM: Twitter URL

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .