Factory Pattern with Angular JS and ES6

angularjs

Introduction

Remember the “Gang of Four”? For those of you who don’t know, the “Gang of Four” is a group of four programmers who wrote a famous computer programming book called Design Patterns: Elements of Reusable Object-Oriented Software. In this book they talk about finding re-usable solutions to common programming problems. The Factory Pattern is a method of creating new objects without exposing the creation logic to the outside code.

Getting started

In order to implement the Factory Pattern in Angular I am going to use Angular’s factory recipe, which creates a new service using a given function. The code will be written in ES6 for simplicity but it can be easily implemented in plain old JS.

We start by creating a base object. Let’s call it Animal.

/**
 * This will be the base class for our animals.
 */
export default class Animal {
    /**
     * Construct a new Animal object
     * @param sound - The sound this animal makes.
     */
    constructor(sound) {
        this._sound = sound;
    }

    /**
     * Logs the animal sound to the console.
     */
    talk() {
        console.log(`${this.sound} !!!`);
    }

    get sound() {
        return this._sound;
    }

    set sound(value) {
        this._sound = value;
    }
}

The next step is to create concrete implementations for our animals.

import Animal from './Animal';

/**
 * Cat extends the Animal object. It will inherit all methods and properties from it.
 */
export default class Cat extends Animal {
    /**
     * Construct a new cat object
     * @param sound - Default cat sound is "meow"
     */
    constructor(sound = "meow") {
        super(sound);
    }
}
import Animal from './Animal';

/**
 * Duck extends the Animal object. It will inherit all methods and properties from it.
 */
export default class Duck extends Animal {
    /**
     * Construct a new cat object
     * @param sound - Default duck sound is "quack"
     */
    constructor(sound = "quack") {
        super(sound);
    }
}

Next we need to create the AnimalFactory. This class will be in charge of creating cats or ducks based on the instructions received.

import Cat from './Cat';
import Duck from './Duck';

export default class AnimalFactory {
    /**
     * This is a static method. This means we can call this method directly on the AnimalFactory class.
     * @param animalType - What type of animal we want to create.
     * @returns {Animal} - The created animal object.
     */
    static getAnimal(animalType = "cat") {
        switch (animalType) {
            // If animalType equals "cat" we create a new Cat object.
            case 'cat':
                return new Cat();
            // If animalType equals "duck" we create a new Duck object.
            case 'duck':
                return new Duck();
            // If animalType is not supported we throw an error.
            default:
                throw new Error(`"${animalType}" is not an animal`);
        }
    }
}

Now it’s time to test our code. We need to register AnimalFactory with angular and then we can use it in our angular code to create different animals.

import angular from 'angular';

import AnimalFactory from './AnimalFactory';

// Create an angular application.
const app = angular.module('app', []);

// Register the AnimalFactory
app.factory('AnimalFactory', ()=>{
    return AnimalFactory;
});

// Use the factory anywhere in your angular code.
app.run((AnimalFactory)=>{
    // Outputs "meow !!!" because it will create a cat object.
    AnimalFactory.getAnimal('cat').talk();
    // Outputs "quack !!!" because it will create a duck object.
    AnimalFactory.getAnimal('duck').talk();
    // Outputs "Uncaught Error: "snake" is not an animal".
    AnimalFactory.getAnimal('snake').talk();
});

As you can see, we managed to hide the actual creation of objects from the code that is using it. This is a very simple example but imagine having very complex object creation logic with different conditions. Using the Factory Pattern we are easily able to re-use and extend this piece of code.

P.S: The sample above can be easily moved to TypeScript and used with Angular 2.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s