Airbrake Blog

Creational Design Patterns: Simple Factory

Written by Frances Banks | Mar 22, 2017 7:25:49 PM

Our first leg of the journey through our Guide to Software Design Patterns takes us into the world of creational design patterns, specifically the simple factory pattern. At its core, the simple factory design pattern is a form of abstraction, which hides the actual logic of implementation of an object so the initialization code can focus on usage, rather than the inner workings.

Throughout this article we'll explain, in real world terms, what a simple factory design pattern attempts to do, and then move onto a functional code example in the C# language (though, as with all patterns, this idea can be translated to most any language or platform). Let's get to it!

In the Real World

The keyword of the simple factory pattern is, of course, factory. It's no mistake that this design pattern is named as such, because the core concept is based on the real world of assembly lines and factory work in general. A giant machine that presses metal into cogs is just one component of the factory, and when a customer orders a batch of cogs, he or she is not concerned with how those cogs are made. Instead, the request is merely, "Make me some cogs." The factory then does the work, and unbeknownst to the customer, out comes a batch of ordered cogs.

The printing press is an obvious real world example of the simple factory design pattern in action. Think about the plates, ink, and huge rollers that all work together to create each print of your local newspaper. Prior to the invention of the printing press, if you wanted to create your own newspaper, you'd need to painstakingly craft each character, line, and page, across dozens of double-sided pages. Every single copy would require the same insane level of work (in fact, you'd probably never finish since the next morning might roll around before one copy is completed).

Yet, for modern newspapers, once the plates are made and everything is configured, creating a second copy of that daily edition is no more difficult than creating the first. Hundreds or thousands of copies can be printed with no additional effort, without having to deal with anything beyond the initial creation. This is a common, easy example of the simple factory design pattern in action.

How It Works In Code

Sticking with the printing press analogy a bit, our code sample for the simple factory pattern focuses on books. We'll start with the full code, then we'll explain the components that make up the simple factory pattern, and how they work together:

using System;
using Utility;

namespace SimpleFactory
{
class Program
{
static void Main(string[] args)
{
var book = BookFactory.MakeBook("The Stand", "Stephen King", 823);
Logging.Log("Title: " + book.Title);
Logging.Log("Author: " + book.Author);
Logging.Log("Page Count: " + book.PageCount);
Logging.Log("Cover Type: " + book.CoverType);
Logging.Log("Object Class: " + book.ToString());
}
}

public enum CoverType
{
Digital,
Hard,
Paperback
}

public interface IBook
{
string Author { get; set; }
string Title { get; set; }
int PageCount { get; set; }
CoverType CoverType { get; }
}

public class PaperbackBook : IBook
{
public string Title { get; set; }
public string Author { get; set; }
public int PageCount { get; set; }
public CoverType CoverType { get; private set; }

public PaperbackBook(string title, string author, int pageCount)
{
Title = title;
Author = author;
PageCount = pageCount;
CoverType = CoverType.Paperback;
}
}

class BookFactory
{
public static IBook MakeBook(string title, string author, int pageCount)
{
return new PaperbackBook(title, author, pageCount);
}
}
}

The goal here of our simple factory pattern is to be able to easily create new copies and types of books, without the need to know how those particular underlying classes are implemented. Thus, the beginning of our simple factory pattern starts with an interface (and an enumeration for simplicity):

public enum CoverType
{
Digital,
Hard,
Paperback
}

public interface IBook
{
string Author { get; set; }
string Title { get; set; }
int PageCount { get; set; }
CoverType CoverType { get; }
}

In most object oriented programming languages, an interface is simply a means of describing the behavior or capabilities of an object, without specifying how it executes that behavior. For our IBook interface here, we've created a number of basic members and indicated the default get and set behavior for Author, Title, and PageCount. This informs C# that this interface allows the values of those three members to be both changed (set) and retrieved (get). To make our later code a bit more interesting, we've also added a CoverType member, using the CoverType enum above, which is only gettable. This means that the CoverType value cannot be set by classes which inherit this interface (by default).

With the basic structure of what our Book looks like, we next want to create a unique Book class that uses our IBook interface, but expands on it in some way. For our purpose, we've created a type of Book called the PaperbackBook:

public class PaperbackBook : IBook
{
public string Title { get; set; }
public string Author { get; set; }
public int PageCount { get; set; }
public CoverType CoverType { get; private set; }

public PaperbackBook(string title, string author, int pageCount)
{
Title = title;
Author = author;
PageCount = pageCount;
CoverType = CoverType.Paperback;
}
}

We're able to then use all the members of our IBook interface, but we've specified that, since this is a paperbackbook, we want our PaperbackBook class to be able to privately set the CoverType member. While not relevant to this example, this allows our other classes that inherit IBook, such as HardcoverBook or DigitalBook, to set their own unique value for the CoverType member, just as we've done here in the constructor for PaperbackBook, setting it to CoverType.Paperback.

Now comes the meat of our simple factory, in which we create a BookFactory class, which returns an instance of our IBook interface through the MakeBook() method:

class BookFactory
{
public static IBook MakeBook(string title, string author, int pageCount)
{
return new PaperbackBook(title, author, pageCount);
}
}

The magic is when we want to create a book, as shown in this example code:

var book = BookFactory.MakeBook("The Stand", "Stephen King", 823);
Logging.Log("Title: " + book.Title);
Logging.Log("Author: " + book.Author);
Logging.Log("Page Count: " + book.PageCount);
Logging.Log("Cover Type: " + book.CoverType);
Logging.Log("Object Class: " + book.ToString());

If we were using a normal class, we'd directly call and create a new instance of PaperbackBook: new PaperbackBook("The Stand", "Stephen King", 823). However, by using our BookFactory class in the simple factory pattern, we've abstracted the process of creating our PaperbackBook class. All that logic and implementation can occur behind the scenes, as all we care about is that when we call BookFactory.MakeBook(), an appropriate book is made.

Best of all, notice in the output, the book.ToString() method call shows that the inheritance functioned as expected, so we're getting a PaperbackBook object like we wanted, even though there's no direct knowledge of (or reference to) it in our initializing code:

Title: The Stand
Author: Stephen King
Page Count: 823
Cover Type: Paperback
Object Class: SimpleFactory.PaperbackBook

This is a basic example of the simple factory pattern in action. We could further add new book types, and perform some basic logic within MakeBook to ensure that the proper book type class is generated and returned, but no matter what, our initializing code through our BookFactory doesn't know or care about how that works.