Design patterns in TypeScript: Factory

A Factory hides the details of instantiating objects within a class hierarchy. The client provides some information regarding the object it needs. Based on that information the factory determines which class of objects to instantiate and how to go about it. Factories are typically Singletons but don’t necessarily need to be, and it depends on the implementation needs.

Factories is very common. Anytime you are using a library that manages objects but delegates their construction to you – you are creating a Factory. One such library is generic-pool.

Another example is constructing different objects within a family based on configuration. For example, in a development environment you may want to use one type of database and in production another. In that case, you may choose to specify a configuration parameter that indicates what database driver to instantiate.

Factories in TypeScript

Source: Factory.ts
export interface Product {
    id: string;
    description: string;
    checkInventory(): number ;
    updateInventory(delta:number): void;
}


class ProductImpl implements Product {
    id: string;
    description: string;
    inventory:number;

    constructor(id:string, description:string) {
        this.id=id;
        this.description=description;
        this.inventory=10;
    }
    checkInventory(): number {
        return this.inventory;
    }
    updateInventory(delta:number) {
        this.inventory=this.inventory+delta;
    }
}

export function createProduct(id: string, description: string): Product {
    return new ProductImpl(id, description);
}
Source: client.ts
import * as factory from './Factory';

var myProduct=factory.createProduct("abc123","Design Patterns");
console.log(myProduct.checkInventory());
myProduct.updateInventory(-3);
console.log(myProduct.checkInventory());

Case study: storage connection factory

Hashicorp Vault is a tool for managing secrets. It supports different storage backends. The exact storage backend is not known until the configuration is specified and the tool is started. Hashicorp Vault is written in Go, but as an exercise, we can think about how it manages configurable storage backends.

Source: StorageFactory.ts

Observe that in our example we only export the interface, but not the implementation. We also export an enumeration of storage kinds. There are no implementation details for each storage type in this example because we are focusing on the pattern. The magic happens in the createStorage function.

export interface Storage {
    put(key:string, value:string);
    get(key:string):string;
}

export enum StorageKind {
    Redis,
    InMemory
}

class RedisStorage implements Storage {
    put(key:string, value:string) {
        //... do something
    }

    get(key:string):string {
        return "foo";
    }
}

class InMemoryStorage implements Storage {
    put(key:string, value:string) {
        //... do something
    }

    get(key:string):string {
        return "foo";
    }
}

export function createStorage(kind:StorageKind):Storage {
    switch(kind) {
        case StorageKind.Redis:
            return new RedisStorage();
        case StorageKind.InMemory:
            return new InMemoryStorage();
    }
}

Variations of the pattern

The original “Gang of Four” design patterns book described two more variations of the factory pattern: Factory Method and Abstract Factory. Both patterns can sometimes be found in libraries, but at the end of the day, they are still factories. In my experience, there is no sense in dwelling on the small details, and I am skipping these patterns for this article.

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s