Popular Design Patterns revisited in JavaScript

Subscribe to my newsletter and never miss my upcoming articles

The Module Pattern

This pattern is used to isolate code from the Global scope. The code inside the isolated container can only be accessed if it is revealed. In ES5 writing the module pattern used something called an IIFE (Immediately Invoked Function Expression) and the syntax for this looked like this.

// Structure of an IIFE 
(function (){
 //Declare your private variables and functions here 

 //Used to expose public variables and functions that other modules can use
 return {

 }

})()

So for example

const UICtrl = (function(){

    let stories = [];

    function addStory(story){
        stories.push(story);
        alert("Story added");
    }

    function getStory(id){
        stories.find(story => {
            return story.id === id;
        })
    }

    return {
        addStory: addStory,
        getStory: getStory
    }

})();

UICtrl.addStory({id: 1, title: "This is a story"}); 
UICtrl.getStory(1)

But with the Introduction of ES6 it uses the export and import statement to isolate code in different files. So using ES6 to rewrite the above it would look like this.

//Name of file is called generateStory.js

let stories = [{id: 1, title: "This is a story"}, 
                             {id: 2, title: "Intro to programming"}, 
                             {id: 3, title: "Why exercise?"}
];

//Return all stories
export const getStories = () => stories;

//Add a story and display the story just added to the user
export const addStory = (story, callback, id) => {
            stories.push(story);
            callback(id);
            alert("Story added");
};

//Get a single story object.
export const getStory = (id) => {
        let story = stories.filter(story => story.id === id )
        story.length != 0 ? console.log(story[0]) : console.log("Story not found")

}

To import this file in app.js for example first we would have to a add a module type to our app.js file in index.html

 <script type="module" src="app.js"></script>

And now to use it in app.js we write

import { getStories, addStory, getStory } from './generateStory.js'

document.addEventListener('DOMContentLoaded', () => {
    console.log(getStories());
    addStory({id: 4, title: "Who wants to be a millionaire?"}, getStory, 4);
})

Here we have successfully been able to use the functions we exported from generateStory.js file into our app.js file where we have called them when our page is loaded.

  • getStories() returns all the stories in our array
  • addStory() takes in three arguments an object of the story you want to add, a callback function in this case getStory() and an ID that should be the same id that you created the post with.

  • Notice I used callbacks in this example, its okay to be used like this here but there are other ways this can be written. Check here to see a better way!

The Factory Pattern

One of my favourtites - This pattern can be used to create multiple objects that share similar properties but vary in a few unique properties. In this example we would be using the Factory Pattern to create various membership for different users.

class MembershipFactory{

    createMemeber(name,type){
        let member = {};

        if(type === 'simple') {
            member = new SimpleMember(name);
        }
        else if (type === 'standard'){
            member = new StandardMember(name);
        }
        else if (type === 'premium'){
            member = new PremiumMember(name);
        }
        else {
            member = new NullMember(name)
        }

        member.type = type;

        member.define = function(){
            console.log(`Member (${this.name}) has a ${this.type} membership that cost ${this.cost}`)
        }

        return member;
    }
}

The main class has a constructor with property name, followed by the function to createMembers that takes in two arguments.

  • Depending on the type of membership a different class is created and the name is passed to it.
  • We can also add properties to our member variable by using the syntax member.type, or member.define to add a function which displays information about the member
  • Notice the this keyword used to display information about the user. This refers to the current object. And in this case it would be assumed when we start creating our objects.
  • Finally we return the member.

In the same file, I have created the classes that represent our Membership types. What varies between these classes is the value of the cost. Finally notice the NullMember class we give a default value of null when a type is passed in that is not a valid type.

class SimpleMember {
    constructor(name) {
        this.name = name;
        this.cost = '£15';
    }
}

class StandardMember  {

    constructor(name) {
        this.name = name;
        this.cost = '£25';
    }
}

class PremiumMember {
    constructor(name) {
        this.name = name;
        this.cost = '£35';
    }
}

class NullMember {
    constructor(name) {
        this.name = null
        this.cost = null;
    }
}

Lets see if it works.

let members = []
let factory = new MembershipFactory();

document.addEventListener('DOMContentLoaded', () => {
    members.push(factory.createMemeber('Omotola', 'simple'))
    members.push(factory.createMemeber('Larry', 'standard'))
    members.push(factory.createMemeber('Jude', 'premium'))
    members.push(factory.createMemeber('QBot', 'estient'))

    members.forEach(member => member.define());
})

Output

Untitled.png

Try console logging just the member, what other properties does it show?

The Observer Pattern

You can get really creative with this. This pattern is used to subscribe to events during the lifecycle of your page being loaded or if the user clicks the button etc. The example implementation I have done for this is something we are familiar with on social media. Subscribing/Unsubscribing to channels. First our UI/Code looks like this

Untitled 1.png

We have got two influencers who we would like to subscribe to. The buttons allows us to subscribe, unsubscribe and finally view our subscription user post. How do we do this?

class EventObserver{

    constructor() {
        this.observers = []
    }

    subscribe(fn){
        this.observers.push(fn)
        console.log("%c You are subscribed", "color: black; font-weight: bold; background-color: green")
    }

    unsubscribe(fn){
        this.observers = this.observers.filter(function(item){
            if(item !== fn){
                return item;
            }
        })
        console.log("%c Your subscription has been revoke", "color: black; font-weight: bold; background-color: red")
    }

    fire(){
        this.observers.forEach((item) =>{
            item.call()
        })
    }
}

First we have an EventObserver class that has a construtor with an observers variable set to be an array. Inside this class we gave three methods subscribe, unsubscribe and fire.

  • Subscribe takes in a function which is pushed to our observer's array.
  • Unsubscribe returns only functions that do not match the function that we passed in.
  • Some fancy console.log are added to give more understanding.
  • Finally the fire function allows the user to view recent post for the their subscriptions. This is done by calling each function in the array using .call()

Now to implementing the functions we actually want to call.

const influencer1 = () => {

    const tweets = [{tweet: "This is great, proud moment"}, {tweet: "Love JS"}];

    console.log("%c Recent tweets from influencer1", "color: white; font-weight: bold; background-color: black;")
    tweets.forEach((post => console.log(post.tweet)))

}

const influencer2 = () => {
    const tweets = [{tweet: "Whats the difference between time and effort?"}];

    console.log("%c Recent tweets from influencer2", "color: white; font-weight: bold; background-color: black;")
    tweets.forEach((post => console.log(post.tweet)))
}

These two functions simply display a list of post by each 'influencer'

Now to use this we write the code like this.

const event = new EventObserver();

//Subscribe
document.querySelector(".if1").addEventListener('click', () => {
    event.subscribe(influencer1);
})

document.querySelector(".if2").addEventListener('click', () => {
    event.subscribe(influencer2);
})

//Unsubscribe
document.querySelector(".unif1").addEventListener('click', () => {
    event.unsubscribe(influencer1);
})

document.querySelector(".unif2").addEventListener('click', () => {
    event.unsubscribe(influencer2);
})

//Call all functions in subscription list.
document.querySelector(".fire").addEventListener('click', () => {
    event.fire();
})

The result

ezgif-6-c3a0cf0ad993.gif

Alright I dont want to make this too long... So there might be a part two.... depending on the reaction to this post! Is there a pattern you would want me to write about? If this was helpful please

LIKE, SHARE AND LEAVE A COMMENT —- MEANS A LOT

You might find these other post interesting if you have read this far.

How to use Object Oriented Programming in JavaScript

Learning JavaScript? My Dating Analogy for CallBacks / Promises / Async & Awaits

Learning JavaScript? Series - Debugging like a pro using Console.log()

You might want to skip this section...... JUST AN APPRECIATION TEXT - Finally completed the #2articles1week challenge today! It was very challenging and exciting taking it on, special thanks to Victoria Lo, Bolaji Ayodeji and Tapas Adhikary for the feedback and post reshares

Dinys Monvoisin's photo

Omotola Shogunle, you are killing each and every article about JavaScript. I love reading your blogpost. I cannot wait for the next one.

I will like to have some clarification about the Factory Design pattern.

I thought that the factory class should be independent and not be inherited by another class. I do not see the point of passing name to the constructor of MembershipFactory.

A tip that I can give you for putting a screenshot of VScode in your blog post will be to use Screenshot mockup.

See the picture below, which is now more elegant.

screenshot-mockup.png

Show +2 replies
Omotola Shogunle's photo

Dinys Monvoisin Thank you! Yes I absolutely see your point and it definitely makes sense to do it that way. When I was researching on this topic the examples I saw used functional programming to make a factory pattern. I thought it would a nice challenge to use classes instead.

Thank you I would make the changes!

Dinys Monvoisin's photo

If you want to make use of OOP. You could make a parent class call Membership for PremierMemberShip, StandardMembership and so forth. Because you are repeating yourself with the name property.

I am just sharing my opinion about the subject do not take me wrong.

I am here learning too.

Bolaji Ayodeji's photo

You're amazing Omotola Shogunle.

Keep them coming, we love your articles 🎊

Omotola Shogunle's photo

Thank you, I would definitely try to keep them coming.

Mohd Shad Mirza's photo

This was great. You got everything covered. Bookmarked 🎉

Chris Bongers's photo

Wow, nice article, really helps me understand these patterns better.

Omotola Shogunle's photo

Yes I am glad this helped. Doing my happy dance 💃

Tapas Adhikary's photo

Congratulation Omotola Shogunle on completing the challenge. This is very inspiring. Your articles are always amazing to read..keep it up!

Omotola Shogunle's photo

Thanks Tapas Adhikary great support from the community is always appreciated.

Victoria Lo's photo

Another wonderful article! Congrats on completing the #2Aritcles1Week challenge! So proud~~

P.S. The code below 'The Factory Pattern' has createMemeber(name,type){..., the word Member is mispelled.

Show +1 replies
Victoria Lo's photo

Yay that's great to hear Omotola Shogunle ! But don't stress too much abt these small typos. I'll be your 2nd pair of eyes haha~