Implement the Observer Pattern in TypeScript

Paul Galvin
6 min readOct 14, 2015

--

[Hi there — I recently published a free book on TypeScript, Yet Another TypeScript Book, that you can read here: https://www.gitbook.com/book/pagalvin/yet-another-typescript-book/details.]

I’ve been making the case to people in my little corner of the world that TypeScript lets us front end developers implement tried and true software design patterns in a more “natural” way than we can with plain old JavaScript. This post describes how to implement the Observer pattern. This is strongly influenced by the book Head First Design Patterns by Elisabeth and Eric Freeman and associates. (I strongly recommend checking it out if you’re want to get started on a more pattern-oriented way of coding). I’m not going to spend any time describing the pattern other than providing the Freemans’ definition:

The Observer Pattern defines a one-to-many dependency between objects so that when one object changes state, all of its dependents are notified and updated automatically.

I first re-invented this wheel back in the late 90’s using a technology stack provided by Progress Corporation. I have done it several times since :).

All of the code for this solution is up on Github at: https://github.com/pagalvin/Patterns. As of now, I have only built out the Observer pattern, but I may do more in future.

This particular solution implements the Observer Pattern two ways: 1) As interfaces that must be fully implemented by all subjects and observers and 2) as a base “Publisher” class that can be extended as / if needed. I think that normally, you implement the interfaces (“favor composition over inheritance”) but there are surely cases where inheritance is at least not a terrible idea.

Interface-style Implementation

We need two interfaces in order to implement the Observer pattern:

  1. “Observable” — this is the thing that clients observe. It is sometimes called the subject.
  2. “Observer” — this is a class that wants to be notified when the subject’s state changes in an interesting way.

Here is IObservable:

https://github.com/pagalvin/Patterns/blob/master/Observer/Interfaces/IObservable.ts

The IObservable interface defines three public methods:

  • RegisterObserver(): A client class registers its interest in the subject’s state changes
  • RemoveObserver(): The counterpoint to RegisterObserver.
  • NotifyObservers(): The subject invokes this method when it has an interesting change in state to communicate to its registered observers (if any).

As you can see, we also need an IObserver interface since register/remove need to know that. In addition, you’ll see that the implementation of NotifyObservers() needs a method to invoke and drop off that interesting state change:

https://github.com/pagalvin/Patterns/blob/master/Observer/Interfaces/IObserver.ts

The IObserver interface only defines ReceiveNotification(). This generic method does not return anything to the caller.

Let’s put this to work. Imagine that you have a weather service object that is receiving weather updates periodically. It doesn’t matter how those updates are received, just that they happen. Various clients want to know about those updates and so they register with the weather service. Here’s some code to show how that happens.

First, we need the weather service object itself:

https://github.com/pagalvin/Patterns/blob/master/Observer/BusinessObjects/WeatherDataServer.ts

The WeatherDataServer object implements the IObservable interface, which means that it must implement the three methods of that interface. In this case, it tracks observers in a private array. Register adds to the array. Remove splices a client out of it and Notify invokes the ReceiveNotification() method on all of its observers.

It doesn’t make a lot of sense to have a subject without someone observing it, and here it is:

https://github.com/pagalvin/Patterns/blob/master/Observer/BusinessObjects/WeatherDataClient.ts

Note that the static NextClientId at line 8 is only present in this class to help me eyeball results more easily in the debugger. The real point is that WeatherDataClient implements the IObserver interface and implements ReceiveNotification() which in turns expects a string.

To test it out, create a bit of JS thusly:

https://github.com/pagalvin/Patterns/blob/master/Observer/Main/ObserverPatternInAction.ts

This naïve little test instantiates a “server” at line 10. It then creates some imaginatively named clients at line 12.

At line 16, we register our clients with the server and finally do a notification at line 21.

We test the remove functionality at line 23 and then verify that it worked on line 25.

This is what things look like in Chrome’s debugger:

You can see that they each received their notification and that client #1 was removed and didn’t receive a message in the 2nd broadcast from the subject.

Inheritance-style Implementation

In this approach, we define a BasePublisher class. I’ve elected to use the IObservable interface to define the required functionality. This is what it looks like:

https://github.com/pagalvin/Patterns/blob/master/Observer/BusinessObjects/Publisher.ts

BasePublisher looks a lot like WeatherDataServer — it implements RegisterObserver() and RemoveObserver() using the same approach as the previous class. However, since this is meant to be a generic publisher of events or other data, it can’t really implement a useful NotifyObservers() method on its own. (It could perhaps supplement a specific client with log messages for instance, and that could be very interesting, but let’s not get crazy). No one should ever call this directly so I’m alerting out a message to that effect. This is actually a good use case for an abstract class (which are available starting in TypeScript v1.6) but my IDE isn’t configured for 1.6 as yet. Soon!

Now that we have BasePublisher in hand, it’s time to create a class that actually implements a meaningful publisher. Let me introduce you to the ScoreDataServer class:

https://github.com/pagalvin/Patterns/blob/master/Observer/BusinessObjects/ScoreDataServer.ts

ScoreDataServer extends BasePublisher at line 3. His constructor takes in an array of scores and that’s that for initialization.

It provides an override of the BasePublisher’s ReceiveNotification() method, sending the array of scores to all of the registered observers at line 15.

The ScoreDataClient is nearly identical to the WeatherDataClient from above:

https://github.com/pagalvin/Patterns/blob/master/Observer/BusinessObjects/ScoreDataClient.ts

The only real difference is that his ReceiveNotification() on line 12 expects an array of numbers instead of a string.

We can test this out as shown:

https://github.com/pagalvin/Patterns/blob/master/Observer/Main/ObserverPatternInAction.ts

This is the same as for the weather test. Create a server, a few clients to observe it, notify, remove an observer and notify again. In Chrome, it looks like this:

And that’s that!

Summary

In summary, this post describes how to implement the Observer Pattern in TypeScript. It uses a pair of interfaces to describe the responsibilities for the subject and its potential observers. It then implements the solution two ways: via a straight-out concrete class that implements the interfaces and alternatively, via a base class that implements the registration/de-registration logic from a base Publisher but leaves the notification logic up to a sub-class to implement.

Happy coding!

</end>

--

--

Paul Galvin

Author and Practice Director @ Neudesic/IBM. Father, Not-Great-But-Always-Trying Zen Buddhist. “Yet Another TypeScript Book” (https://amzn.to/2ABntAX).