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
}
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))
}
}
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);
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
}
}
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)
})
}
}
}
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);
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: