Introduction to the Factory Pattern

I have written in the past about other design patterns. In this article, I will tell you about another popular one, the factory class pattern, and when it's appropriate to use.

I have written in the past about other design patterns, why knowing they exist and using them is important. In this article, I will tell you about another popular one, the factory class pattern.

Some of the biggest benefits of Object Oriented Programming are code reuse and organization using polymorphism. This powerful tool allows you to create an abstract Animal class and then override some of that behavior in your Parrot class. In most languages, you can create an instance of an object using the new keyword. It is common to write your code anticipating that you'll require an Animal at run-time, without the benefit of foresight as to whether it will be a Dog or a Frog. For example, imagine that your application allows you to select your favorite animal via a configuration file, or that you gather that information by prompting the user of the application. In that scenario, when you write the code you cannot anticipate which animal class will be instantiated.  Here's where the factory pattern comes in handy.

The Animal Factory

In the factory pattern, there is a method that will take those external factors as input and return an instantiated class. That means that you can defer the creation of the object to some other piece of code and assume you'll get the correct object instance from the factory object or method. Essentially, the factory couples logic from external factors with the list of eligible classes to instantiate.

In the factory class pattern it is common to end with class names like UserInputAnimalFactory or ConfigAnimalFactory. However, in some situations you may need to decide what factory to use during runtime, so you need a factory of factories like ConfigAnimalFactoriesFactory. That may sound confusing, but it is not uncommon.

I have created a nodejs package that will help to illustrate the factory pattern. It is called easy-factory, and you can download it from npm with npm install --save easy-factory. This package contains a factory base class that you can extend. In your extending class you only need to implement a method that contains the business logic necessary to decide which class should be used.

Let’s assume that your app offers the users a list of animal sounds. The user is also asked to select a size. Based on the sound name and the size, the app will print the name of the selected animal. The following code is what this simple app would look like.

// Import the animal factory. We'll get into it in the next code block.
const SoundAnimalFactory = require('./SoundAnimalFactory');
const soundNames = ['bark', 'meow', 'moo', 'oink', 'quack'];
// The following code assumes that the user chooses one of the above.
const selectedSoundName = scanUserAnimalPreference();
// Have the user select the animal size: 'XS', 'S', 'M', 'L', 'XL'.
const selectedSizeCode = scanUserSizePreference();
// Based on the sound name and the size get the animal object.
const factoryContext = {sound: selectedSoundName, size: selectedSizeCode};
const animal = SoundAnimalFactory.create(factoryContext, selectedSizeCode);
// Finally, print the name of the animal.
print(animal.getName());

There is no new keyword to create the animal object. We are deferring that job to SoundAnimalFactory.create(…). Also notice, we are passing two variables to the factory: factoryContext and selectedSizeCode. The first parameter is used to determine which class to use, the rest of the parameters will be passed to that class constructor when calling with the new keyword. Thus, with factoryContext the factory will have enough information to create the instance of the needed object. selectedSizeCode will be passed to the constructor of the determined class. Thus ultimately you will end up with something like new Cat('S').

The code for the factory will be aware of all the available animals and will inspect the factoryContext to decide which one to instantiate.

// SoundAnimalFactory.js
'use strict';

const FactoryBase = require('easy-factory');

class SoundAnimalFactory extends FactoryBase {

 /**
  * Decide which animal to instantiate based on the size and sound.
  *
  * @param {object} context
  *   Contains the keys: 'size' and 'sound'.
  *
  * @throws Error
  *   If no animal could be found.
  *
  * @return {function}
  *   The animal to instantiate.
  */
 static getClass(context) {
   if (typeof context.size === 'undefined' || typeof context.sound === 'undefined') {
     throw new Error('Unable to find animal.');
   }
   if (context.sound === 'bark') {
     return require('./dog');
   }
   else if (context.sound === 'meow') {
     if (context.size === 'L' || context.size === 'XL') {
       return require('./tiger');
     }
     return require('./cat');
   }
   // And so on.
 }

}

module.exports = SoundAnimalFactory;

Notice how our application code executed the method SoundAnimalFactory.create but our factory only implements SoundAnimalFactory.getClass. That is just because in this particular implementation of the factory pattern the actual instantiation of the object takes place in the base class FactoryBase. What happens behind the scenes is that the create method will call getClass to know which class to implement and then call new on that with any additional parameters. Note that your approach may differ if you don't use the easy-factory nodejs package.

Real-World Example

I use this pattern in almost all the projects I am involved. For instance, in my current project we have different sources of content since we are building a data pipeline. Each source contains information about different content entities in the form of JSON documents that are structured differently for each source. Our task is to convert incoming JSON documents into a common format. For that, we have denormalizer classes, which take the JSON document in a particular format and transform it to the common format. The problem is that for each combination of source and entity type, we need to use a different denormalizer object. We are using an implementation similar to the animal sound factory above. Instead of taking user input in this case, we inspect the incoming JSON document structure instead to identify the source and entity type. Once our factory delivers the denormalizer object, we only need to call doStuffWithDenormalizer(denormalizer, inputDocument); and we are done.

// The input document comes from an HTTP request.
// inputDocument contains a ‘type’ key that can be: season, series, episode, …
// it also contains the ‘source’ parameter to know where the item originated.
const inputDocument = JSON.parse(awsSqs.fetchMessage());

// Build an object that contains all the info to decide which denormalizer
// should be used.
const context = { type: inputDocument.type, source: inputDocument.source };
// Find what denormalizer needs to be used for the input document that we
// happened to get from the AWS queue. The DenormalizationFactory knows which
// class to instantiate based on the `context` variable.
const denormalizer = DenormalizationFactory.create(context);
// We are done! Now that we have our denormalizer, we can do stuff with it.
doStuffWithDenormalizer(denormalizer, inputDocument);

You can see how there are two clear steps. First, build an object –context that contains all the information to know which denormalizer to instantiate. Second, use the factory to create the appropriate instance based on the context variable.

As you can see this pattern is very simple and a common way to get an instance of a class without knowing which class needs to be instantiated when you are writing the code. Even if simple it is important to identify this approach as a design pattern so you can communicate more efficiently and precisely with your team.

Published in:

Get in touch with us

Tell us about your project or drop us a line. We'd love to hear from you!