Design Pattern: Observer Pattern in TypeScript

Observer pattern decouples the subject from the observer/subscriber. Using this pattern the subject publisher can trigger events on some change, and the listener/subscriber gets informed of the change.

This article demonstrates Observer pattern implementations in TypeScript. Check the following examples.

Implementation

Here is how to implement the Observer pattern in TypeScript step-by-step:

  • Define a subject interface. Decare methods for attaching a observer to the subject, and to notify subscribers/observers.
  • Define a concrete subject. Implement the subject interface for the concrete subject class. Maintain a list of subscribers/observers in this class.
  • Define an abstract observer. Declare a property to holding reference to the subject and declare a method to send state updates.
  • Define observer classes and extend the abstract observer. Set/attach the subject in constructer and define method for sending update.

Examples

Check the following examples of Observer pattern implementation.

Example #1: General Observer

Here is an example of a general observer.

Subject Interface

  • Create file “subject.ts”.
  • Define interface “Subject”.
  • Declare method “getState” and “setState” for getting and setting the state change. 
  • Declare method “attach”, which will be used to attach an observer to the subject.
  • Declare method “notifyObservers”. This will be used to notifiy all observers about the state change. This is the publish event method.
// subject.ts

import Observer from "./observer";

interface Subject {
    getState(): number;
    setState(state: number): void;
    attach(observer: Observer): void;
    notifyObservers(): void;
}

export default Subject;

Concrete Subject

  • Create file “concrete-subject.ts”.
  • Define class “ConcreteSubject”.
  • Define states or properties as per requirement. Here we have only one state/property named “state”.
  • Define an array field of type “Observer”. This is used to store the list of all observers.
  • Implement “Subject” interface for this class.
  • In the method “attach” add a new observer to “observerList”.
  • In “notifyObservers”, loop through the “obseverList” and call “sendUpdate” of the observer.
// concrete-subject.ts

import Observer from "./observer";
import Subject from "./subject";

class ConcreteSubject implements Subject {
    private state: number = 0;
    private observerList: Observer[] = [];

    getState(): number {
        return this.state;
    }

    setState(state: number): void {
        this.state = state;
        this.notifyObservers();
    }

    public attach(observer: Observer): void {
        this.observerList.push(observer);
    }

    public notifyObservers(): void {
        for (let observer of this.observerList) {
            observer.sendUpdate()
        }
    }
}

export default ConcreteSubject;

Abstract Observer

  • Create file “observer.ts”.
  • Define abstract class “Observer”.
  • Define the field “subject” of type “Subject”.
  • Declare abstract method “sendUpdate”.
// observer.ts

import Subject from "./subject";


abstract class Observer {
    protected subject: Subject | null = null;

    abstract sendUpdate(): void;
}

export default Observer;

Observer One

  • Create file “observer-one.ts”.
  • Define the observer class named “ObserverOne”.
  • Extend “Observer” for “ObserverOne”.
  • In the constructor accept a “Subject” param. Then set the subject field and call the “attach” method of the subject to attach the observer to the subject.
  • Define the method “sendUpdate”.
// observer-one.ts

import Observer from "./observer";
import Subject from "./subject";


class ObserverOne extends Observer {
    constructor(subject: Subject) {
        super();

        this.subject = subject;
        this.subject.attach(this);
    }

    sendUpdate(): void {
        console.log("Received in ObserverOne: " + this.subject?.getState());
    }
}

export default ObserverOne;

Observer Two

  • Create file “observer-two.ts”.
  • Define the observer class “ObserverTwo”.
  • Extend “Observer”. In the constructor accept a “Subject” param. Then set the subject field and call the “attach” method of the subject to attach the observer to the subject. Also, define method “sendUpdate”.
// observer-two.ts

import Observer from "./observer";
import Subject from "./subject";

class ObserverTwo extends Observer {
    constructor(subject: Subject) {
        super();

        this.subject = subject;
        this.subject.attach(this);
    }

    sendUpdate(): void {
        console.log("Received in ObserverTwo: " + this.subject?.getState());
    }
}

export default ObserverTwo;

Demo

For using the implementation create an object of “ConcreteSubject” named “subject”.

Initiate “ObserveOne” and “ObserverTwo” and send the “subject” to those observers. This will attach the observers to the subject.

Call the “setState” of the subject, which in turn will notify the observers about the state change.

// demo.ts

import ConcreteSubject from "./concrete-subject";
import ObserverOne from "./observer-one";
import ObserverTwo from "./observer-two";

var subject = new ConcreteSubject();
new ObserverOne(subject);
new ObserverTwo(subject);

console.log("Setting subject value to 10");
subject.setState(10);


console.log("Setting subject value to 999");
subject.setState(999);

Output

We will get the following output from the above implementation-

Setting subject value to 10
Received in ObserverOne: 10
Received in ObserverTwo: 10


Setting subject value to 999
Received in ObserverOne: 999
Received in ObserverTwo: 999

Source Code

Use the following link to get the source code:

Other Code Implementations

Use the following links to check Observer pattern implementation in other programming languages.

Leave a Comment


The reCAPTCHA verification period has expired. Please reload the page.