Design patterns: Factory

Design patterns (4 part series)

  1. Design patterns: Singleton
  2. Design patterns: Factory
  3. Design patterns: Observer
  4. Design patterns: Decorator

Yo yo! This is the second post that I’m writing as I’m starting to learn design patterns. My first post covered the singleton pattern and you can find that here. This time we’ll be taking a look at another creational pattern, the factory pattern. Let’s first visit the definition, then the implementation of the pattern, and finish with a look at some example code.

Overview

  • Definition
  • Implementation
  • Example - Creating books

Definition

Factory: a building or group of buildings where goods are manufactured.

Like I said earlier, the factory pattern is a creational pattern, and it gives us flexibility in creating different types of objects that inherit from the same class and then use those objects within the same code.

We use a factory and product relationship to make up this pattern. The factory takes care of instantiating the products that it wants to produce, and the products just need to know what rules they need to follow in order to be a product.

Implementation

To achieve the factory pattern, there are 4 types of classes we need to implement:

  1. Abstract Factory class

    • Defines an abstract Create method. In some cases, this may include a default implementation.
  2. Concrete Factory subclasses

    • Implement the Create method (these will instantiate the Factory's Product objects).
  3. Abstract Product class

    • Defines the Product properties and methods.
  4. Concrete Product subclasses (these will be instantiated by the Factory objects)

    • Inherit from the Product class and define the unique Product types.

This pattern allows us to defer the creation of the Product objects to the subclasses of Factory, making it a creational pattern that gives our code flexibility.

Using C#, the pattern looks like this:

using System;
public class Program
{
public static void Main()
{
FactoryA a = new FactoryA();
FactoryB b = new FactoryB();
Console.WriteLine(a + " created " + a.Create().Name);
Console.WriteLine(b + " created " + b.Create().Name);
}
}
abstract class Factory
{
public abstract Product Create();
}
class FactoryA : Factory
{
public FactoryA(){
}
public override Product Create()
{
return new ProductA();
}
}
class FactoryB : Factory
{
public FactoryB(){
}
public override Product Create()
{
return new ProductB();
}
}
abstract class Product
{
public string Name {get; set;}
}
class ProductA : Product
{
public ProductA()
{
this.Name = "Pepsi";
}
}
class ProductB : Product
{
public ProductB()
{
this.Name = "Coke";
}
}

Example - Creating books

Now, let’s take a look at a real world example. Perhaps we want to create a program that helps users create different types of books. For now, this program will help the user create a Novel, or a HistoryBook.

Novels will be composed of pages: title page, event, and about the author.

History books will be composed of pages: acknowledgments, units, and glossary.

The program will then print out the pages of the book that the user decided to create (using the same code no matter what book type that the user chose).

using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
Console.WriteLine("Welcome to Book creator!");
Console.WriteLine("Let's make a Novel");
string bookType = "Novel"; // Try replacing this with "History"
BookFactory bookFactory = new CoolBookFactory(bookType);
Book userBook = bookFactory.Create();
// The magic of the pattern is below:
Console.WriteLine("PAGES -----");
foreach (var page in userBook.pages)
{
Console.WriteLine(page.GetType().Name);
}
}
}
abstract class BookFactory
{
public abstract Book Create();
}
class CoolBookFactory : BookFactory
{
public string BookType { get; private set; }
public CoolBookFactory(string bookType)
{
this.BookType = bookType;
}
public override Book Create()
{
if (this.BookType == "Novel")
return new NovelBook();
if (this.BookType == "History")
return new HistoryBook();
return null;
}
}
abstract class Book
{
public List<Page> pages = new List<Page>();
}
class NovelBook : Book
{
public NovelBook()
{
this.pages.Add(new TitlePage());
this.pages.Add(new EventPage());
this.pages.Add(new AboutTheAuthorPage());
}
}
class HistoryBook : Book
{
public HistoryBook()
{
this.pages.Add(new AcknowledgmentsPage());
this.pages.Add(new UnitsPage());
this.pages.Add(new GlossaryPage());
}
}
abstract class Page {}
class TitlePage : Page {}
class EventPage : Page {}
class AboutTheAuthorPage : Page {}
class AcknowledgmentsPage: Page {}
class UnitsPage : Page {}
class GlossaryPage : Page {}

You can see that we now have the flexibility of adding other books and pages without modifying the creation code written in our main class. To add other books, all we would need to do is have the Factory class handle another book type.

Disclaimer: There are a lot of resources for learning design patterns, and they can be implemented in different ways. I would recommend exploring more resources when finished with this post.

Attributions:
  1. undefined by undefined