JavaScript and Design Patterns - Chapter 3 πŸš€

Cover Image for JavaScript and Design Patterns - Chapter 3 πŸš€
Lazar Stankovic
Lazar Stankovic

πŸ€“ Intro


In this chapter we will explore the depths of the Observer Pattern and unlock its potential to enhance your JavaScript knowledge.

πŸ‘€ About bserver Pattern


The Observer Pattern is a behavioral design pattern that lets you define a subscription mechanism to notify multiple objects about any events that happen to the object they're observing. In other words, the Observer Pattern defines a one-to-many type of dependence between different objects, and it ensures that the change of the state in one object gets automatically reflected in all dependant objects.


ENTITIES


Entities that will participate in the Observer paradigm are:


- Subject - It keeps a reference towards its observer. One object can have many Observer objects It provides an interface for "attaching" and "detaching" observer objects

- Concrete Subject - It keeps the state of interest of the Concrete Observer objects It sends notifications to its observers when the state changes

- Observer - It defines an interface for updating the objects after the state of the changes in the objects of the class type Subject

- Concrete Observer - It keeps a reference to the Concrete Subject objects It keeps the state that should stay consistent with the state of the parent class It implements an interface for updating object that is defined in the Observer class


Let's see the code...

1//The subject class
2//It provides an interface
3//for "attaching" and "detaching" 
4//observer objects
5class Subject{
6    constructor(){
7        this.observerList = [];
8    }
9    attach(observer) { 
10        console.log(`πŸ“Ž Attaching observer... ${observer.name}`);
11        this.observerList.push(observer);
12    }
13    detach(observerId) {
14        let observer = this.observerList.find(item => item.id === observerId);
15        console.log(`πŸ”— Detaching observer... ${observer.name}`);
16        this.observerList = this.observerList.filter(item => item.id !== observerId)
17    }
18    notify() { 
19        console.log('🟒The Notification process starts...');
20        for (let i = 0; i < this.observerList.length; i++) {
21            this.observerList[i].update()
22        }
23        console.log('πŸ”΄The Notification process ends...');
24    };
25}
26//It keeps the state of interest of the Concrete Observer objects
27//It sends notifications to its observers when the state changes
28class ConcreteSubject extends Subject {
29    constructor(){
30        super();
31    }
32    //return subject state
33    getSubjectState(){ return this.subjectState; }
34    //set new subject state
35    setSubjectState(subjectState){ this.subjectState = subjectState; }
36    function(){
37        return { getSubjectState, setSubjectState }
38    }
39};
40//It defines an interface for updating the objects after the state 
41//of the changes in the objects of the class type Subject 
42class Observer{
43    update() { return; }
44};
45//The concrete observer class will extend an observer class
46//It keeps a reference to the Concrete Subject objects
47//It keeps the state that should stay consistent with the state of 
48//the parent class
49class ConcreteObserver extends Observer{
50    constructor(id, subject, name){
51        super();
52        this.id = id;
53        this.subject = subject; //a reference to the Concrete Subject object
54        this.name = name;
55        this.observerState = ""; // the state that should stay consistent with the state of
56                                 //the parent class
57    }
58
59    //The interface for update object
60    update() { 
61        this.observerState = this.subject.subjectState; 
62        console.log(`${this.name} new state is: ${this.observerState}`);
63    }
64
65   getSubject() {
66        return this.subject;
67    }
68
69    setSubject(subject){
70        this.subject = subject;
71    }
72};
73//we will instantiate our concrete subject class that should be observed
74const s = new ConcreteSubject();
75//attaching the first concrete observer with an id of 1, 
76//passing the concrete subject instance
77//passing the name of the observer
78s.attach(new ConcreteObserver(1, s, 'First observer'));
79//attaching the second observer
80s.attach(new ConcreteObserver(2, s, 'Second observer'));
81//changing the subject state
82s.setSubjectState("subject changed");
83//notifying OBSERVERS (we have two observer at the moment) that the state was changed
84s.notify();
85/* OUTPUT OF THE NOTIFY FUNCTION
86🟒 The Notification process starts...
87First observer new state is: subject changed
88Second observer new state is: subject changed
89πŸ”΄ The Notification process ends...
90*/
91//detaching the observer with an id of 1
92s.detach(1)
93//changing the subject state
94s.setSubjectState("removed one observer");
95//notifying OBSERVER (because we detached our observer with an id of 1
96//we are left with only one observer that should get notified about the
97//change of the state
98s.notify();
99/* OUTPUT OF THE NOTIFY FUNCTION
100🟒 The notification process starts...
101Second observer new state is: removed one observer
102πŸ”΄ The Notification process ends...
103*/

Please try executing this code, it is always better to look it in the text editor of your preference, and of course, play with it, try adding one more subject and as many observers as you want.


Here is the UML diagram for the visual learners.


[@portabletext/react] Unknown block type "image", specify a component for it in the `components.types` prop


🌎 Real world analogy


πŸ“° NEWSPAPER / MAGAZINE ANALOGY


If you subscribe to a newspaper or magazine, you no longer need to go to the store to check if the next issue is available. Instead, the publisher sends a new issue directly to your mailbox right after publication or even in advance. The publisher maintains a list of subscribers and knows which magazines they are interested in. Subscribers can leave the list at any time when they wish to stop the publisher from sending new magazine issues to them.


πŸ“ˆ STOCK ANALOGY


The observer pattern also applies to the real world in terms of the stocks. For example, you can have an investor, a company for example Apple. Apple has stocks, investors invest their πŸ’° into the Apple stocks, thus, they would certainly like to be notified when the stocks change so they can turn their strategy around.
In this case, a Stock is a Subject, Apple stock is a Concrete Subject, Investor is an Observer, and let's say Warren Buffet is a Concrete Observer.


❓ WHEN TO USE THE OBSERVER PATTERN?


You could use the observer pattern when changes to the state of one object may require changing other objects, and the actual set of objects is unknown beforehand or changes dynamically.


Use the pattern when some objects in your app must observe others, but only for a limited time or in specific cases.


βš™ RxJS OBSERVABLE (ANGULAR)


The observer pattern is a software design pattern in which an object, called 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.


The observer pattern is the pattern that RxJS observable is using.


Observable β€” this is the thing that clients observe, it is sometimes called the subject.


Observer β€” this is a class that wants to be notified when the subject’s state changes in an interesting way.


An Example of creating observables:

1import { Observable } from 'rxjs';
2
3const observable = new Observable(function subscribe(observer) {
4  var id = setInterval(() => {
5    observer.next('hi')
6  }, 1000);
7});


An Example of subscribing to an observable

1observable.subscribe(x => console.log(x));
2


When calling observable.subscribe with an Observer, the function subscribe in Observable.create(function subscribe(observer) {...}) is run for that given Observer. Each call to observable. Subscribe triggers its own independent setup for that given Observer.


βœ… PROS


- Open/Closed Principle. You can introduce new subscriber classes without having to change the publisher’s code (and vice versa if there’s a publisher interface).

- You can establish relations between objects at runtime.


❌ CONS


- Subscribers are notified in random order.


πŸ™ THANK YOU FOR READING!