Code on a screen

Observer – A Tool for State Management

Introduction

 This is the second in our Software Design Pattern series of posts and today we are going to take a look at the Observer pattern.  The formal intent of the Observer Pattern as defined in the GOF Design Pattern book is, “Define a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.”  This pattern is actually used quite often, for example when you create a listener on an element in JavaScript for when that element is clicked.  Anytime you have a situation where one object needs to know when another object’s state changes, you can use the Observer pattern.

Pattern Overview

 There are 4 key elements that you must consider when implementing the Observer Pattern which are as follows:

 Subject – This is an interface that enforces a class type which includes methods for adding and removing observers and notifying observers that have been added and want to know when the state of the Concrete Subject changes.

 Observer – This is an interface that enforces a class type which includes a method that is called to update an object type of this interface when the state of the Concrete Subject changes.

 Concrete Subject – This is the class that a Concrete Observer is interesting in knowing when the state of the Concrete Subject Changes.

 Concrete Observer – This is the class that is interested in knowing when the state of a Concrete Subject changes.

News Feed Example – Design

Sometimes it’s quite challenging to see how something works when you talk in the abstract so here is an example of what it looks like to use the observer pattern to notify interested objects when the state changes in the subject.  We will use a News Feed as our example, it will be our Subject.  The Observers are different kinds of Aggregators such as a Email and RSS that subscribe to the News Feed and want to know when a new article is added to the feed, i.e. when the state of the News Feed changes.  Here is a UML Class Diagram that shows the structure of such a solution:

News Feed Example – Implementation

I have chosen to implement our News Feed example using TypeScript mainly because it offers some key features that are not easily available in Vanilla JavaScript without a lot of work that would obfuscate the explanation of how this works rather than help explain it.  For example TypeScript offeres key Object Oriented features not directly available in Vanilla JavaScript such as Interfaces a key element in the design patterns we’re looking at.  Here we will show the code used to implement the News Feed Observer Pattern example.  If you are interested in playing around with the code here is the whole solution available on CodePen:

See the Pen Observer Pattern Example by brad jones (@bradnjones) on CodePen.dark

First let’s look at the Subject Interface implementation.  In our example we want to manage Subscribers of the News Feed so we have created add(), remove() and notify() methods in the interface type definition which will be implemented by the Concrete Subject our NewsFeed class.

/**
 * Observer Pattern - Subject Interface
 */
interface INewsFeed {
    add(aSubscriber: ISubscriber): void;
    remove(aSubscriber: ISubscriber): void;
    notify(): void;
}

Now let’s look at the Observer Interface implementation.  The code for this interface type definition is very simple the interface has an id property and an update() method.  The id property will be used to identify the Subscriber in our examples output and the update() method will be what is executed on the Subscriber when it is notified that a new Article has been added to the news feed.

/**
 * Observer Pattern - Observer Interface
 */
interface ISubscriber {
    id: number;
    update(): void;
}

Now let’s look at the Concrete Subject Class that implements the INewsFeed interface.  The code for this class is the most complex of the solution because it has two responsibilities, first to manage it’s own state of adding articles to the object using the addArticle() method.  Second, to manage the registration, de-registration and notification of those objects that have subscribed to the NewsFeed via the add(), remove(), and notify() method implementations.  This class therefore contains two private properties the _subscribers Array which contains the list of ISubscriber Objects that have been added to the NewsFeed as well as the _feed Array which contains the list of Articles that have been added to the news feed.  Here is the code:

/**
 * Observer Pattern - ConcreteSubject Class
 */
class NewsFeed implements INewsFeed {
 

    private _subscribers: Array<ISubscriber> = [];
    private _feed: Array<Article> = [];
 

    get feed(): Array<Article> {
        return this._feed;
    }
 

    add(aSubscriber: ISubscriber){
        this._subscribers.push(aSubscriber);
        log(`[NewsFeed] - add new subscriber with id = ${aSubscriber.id}`);
    }
 

    remove(aSubscriber: ISubscriber){
        this._subscribers.splice(this._subscribers.indexOf(aSubscriber), 1);
        log(`[NewsFeed] - remove a subscriber with id = ${aSubscriber.id}`);
    }
 

    notify(){
        this._subscribers.map((thisSubscriber) => {
            thisSubscriber.update();
        })
        log(`[NewsFeed] - notified ${this._subscribers.length} subscribers`);
    }
 

    addArticle(anArticle: Article){
        this._feed.push(anArticle);
        this.notify();
    }
}

Now let’s look at the Concrete Observer Classes that implement the ISubscriber interface.  In this example we have two types of Observers, first an Email Aggregator and second an RSS Aggregator each of which can result in one or more objects that want to be notified when a new article is added to the news feed.  The interesting thing about the observer pattern is that there is a dependency that is explicitly defined between the Concrete Subject and the Concrete Observer where the Concrete Observer contains a parameter of the NewsFeed.  The purpose for this is to encapsulate the way the observer gets to and uses the state of the Subject completely within the observer object.  This is achieved by making the Subject a parameter of the constructor method in the Observer.  In our example we also pass the id as a parameter of the constructor so that it can be identified.  This identification is mainly so we can show that the correct Observer’s are being used in the output of our example.  We have two types of Observers that Aggregate the Articles in the NewsFeed, an EmailSubscriber and an RSSSubscriber.  Their code is as follows:

/**
 * Observer Pattern - ConcreteObserver Class Type 1
 */
class EmailSubscriber implements ISubscriber{
    id: number;
    private _newsFeed: NewsFeed;
 

    constructor(aNewsFeed: NewsFeed, anId: number){
        this._newsFeed = aNewsFeed;
        this.id = anId;
    }
 

    update(){
        log(`[EmailSubscriber] with id = ${this.id} notified.`);
        this._newsFeed.feed.map((thisArticle) => {
            log(`[EmailSubscriber] id = ${this.id} prints article title = ${thisArticle.title}`);
        });
    }
}
/**
 * Observer Pattern - ConcreteObserver Class Type 2
 */
class RssSubscriber implements ISubscriber{
    id: number;
    private _newsFeed: NewsFeed;
 

    constructor(aNewsFeed: NewsFeed, anId: number){
        this._newsFeed = aNewsFeed;
        this.id = anId;
    }
 

    update(){
        log(`[RssSubscriber] with id = ${this.id} notified.`);
        this._newsFeed.feed.map((thisArticle) => {
            log(`[RssSubscriber] id = ${this.id} prints article title = ${thisArticle.title}`);
        });
    }
}

Finally we have the Article Class which isn’t really a key element of the observer pattern but is used in our example to represent Article objects held in the News Feed.  Here is the code:

/**
 * An class created for this example (not a part of the 
 * official observer pattern persay)
 */
class Article {
    private _title: string;
 

    constructor(aTitle: string){
        this._title = aTitle;
    }
 

    get title(): string {
        return this._title;
    }
}

After this code is compiled from TypeScript into Vanilla JavaScript and run either via the command line with Node.js or in a browser, the following output can be seen.  In the output we can clearly see Observers being added to the Subject.  The Observers notified when the state of the Subject changes by adding a new article.  When an Observer is removed, and the state changes again, only the observers still registered with the Subject are notified.  This is a great example to see the Observer Pattern in action.

Criticisms

As you go through this pattern it becomes very apparent that there are may useful situations where it can be used but there are also considerations to be made.  One of the problems with Design Patterns is that when an inexperience Software Engineer learns about a new pattern they think it can be used everywhere not realizing the tradeoffs you may be making.  Sometimes the tradeoffs are worth the benifits brought to the table by the pattern but then, this is the job of a Software Engineer, to make these kind of important decisions.  Some have even gone as far as to suggest we no longer use the Observer Pattern.  Martin Oderski, whom we know from our Singleton Pattern post is not a big fan of Design Patterns, wrote a paper titled, “Deprecating the Observer Pattern” in which he presents a lengthy argument for why you should not use the observer pattern.  It essentially says that the observer pattern violates a large number of core coding principles such as encapsulation, composability, separation of concerns, etc…and therefore it should not be used.  I’m not in complete agreement but do believe that one should know the potential draw backs and make an informed decision when apply a design pattern such as the observer pattern to solve a coding problem. 

For example, some might say that by passing the instance of the Subject to the Observer constructor upon instantiation that you are violating encapsulation, particularly when the Observer is then notified that the state has changed and uses it’s reference to the instance of the Subject to do what it needs to do.  I don’t think that this really violates encapsulation unless the Observer somehow mutates the state of the Subject.  If the Subject is written correctly than no other objects should be able to mutate it’s state.  For me viewing the changed state does not violate encapsulation.

Conclusion

Design patterns exist because there are common design problems that are regularly faced by Software Engineers when they are contemplating how to use a programming language to solve a particular problem.  It is very important to understand not just the valuable solution that a design pattern brings to the table but also the potential pitfalls.  It is the software engineers job to then make an informed decision for the based way forward with all this information to hand.

Read other posts in this series:

Part 1 – Singleton – Pattern or Anti-Pattern?

I love Jesus Christ, my wife and my children. I also really enjoy using technology to help solve peoples problems. This blog will contain my thoughts primarily on these topics. All posts are my own.
Leave a Reply

Your email address will not be published. Required fields are marked *