this pattern is a behavioral pattern, taken from the classic book of the gang of four. bellow is my implementation in typescript with a concrete example.

abstract class Observer {
    abstract update(): void;
}

abstract class Subject {
    abstract subscribe(o: Observer): void;
    abstract unsubscribe(o: Observer): void; 
    abstract notifyAll(): void;
}

Once we have these interfaces, we can go ahead and define the concrete classes:

class Singer extends Subject {

    private _state: Number;
    private _subscribers: Observer[];

    constructor(state = 0) {
        super();
        this._state = state;
        this._subscribers = [];
        console.log("a new singer created, with state: " + this.state);
    }

    subscribe(o: Observer): void {
        this._subscribers.push(o);

        console.log("current subscribers: " + this._subscribers.toString());
    }

    unsubscribe(o: Observer): void {
        const i = this._subscribers.indexOf(o);
        this._subscribers.splice(i, 1);

        console.log("current subscribers: " + this._subscribers.toString());
    }

    notifyAll(): void {
        for(let o of this._subscribers) {
            o.update();
        }
        console.log("All subscribers have been notified!");
    }

    get state() {
        return this._state;
    }

    set state(val: Number) {
        this._state = val;
        this.notifyAll();
    }
}


class Groopie extends Observer {
    private _state: Number = 0;
    private _singer: Singer = null as any;

    update(): void {
        this._state = this._singer.state;
        console.log("state updated: current value: " + this._state);
    }

    get state(): Number {
        return this._state;
    }

    constructor(singer: Singer) {
        super();
        this._singer = singer;
        this._singer.subscribe(this);
        this.update();
    }

}


const slash = new Singer(0);
const jane = new Groopie(slash);
console.log(jane.state);

slash.state = 2;
const john = new Groopie(slash);

console.log(jane.state);
console.log(john.state);

slash.unsubscribe(jane);

slash.state = 8;
console.log(jane.state);
console.log(john.state);

slash.subscribe(jane);

slash.state = 16;
console.log(jane.state);
console.log(john.state);
abstract class Observer {
    abstract update(): void;
}

abstract class Subject {
    abstract subscribe(o: Observer): void;
    abstract unsubscribe(o: Observer): void; 
    abstract notifyAll(): vo...

Play with this code in TypeScript Playground

Variations:

In the next post we will discuss and implement the following scenario: What if the observers can subscribe to multiple subjects?

This arises several issues about redunant notifications, we will see two approaches to this problem.

stay tuned!