7FACTOR NEWS
Build Cool Things Responsibly
By Kyle Sudu
What the heck is the Factory Method Principle, and why should we care about it?
Factory Method Principle (FMP) is a design pattern that involves the use of a ‘factory’ and delegating the task of creating objects to that ‘factory.’ It is a creational pattern part of the ever-important Gang of Four design patterns. It is a pattern often taken for granted by Jr. engineers not used to working at scale. Its benefits begin to shine as programs begin to scale towards 10s of thousands of users or objects are created at a more constant pace. When memory and performance begin to decline, implementing FMP can be a great solution to maximizing current resources. In FMP, Factories are responsible for the encapsulation of the business logic for the creation of an object. It solves the problem of needing to build something without knowing all the details!
The Object-Oriented Programing paradigm is centered around the creation and manipulation of objects. At some point, you must instantiate these objects. In other words, the word ‘new’ needs to be used at some point. We use the FMP to abstract away that use of the word ‘new’. Depending on the situation, there will be different business logic that we will need to create different objects. Under FMP we define a contract (interface or superclass etc.) for creating an object but let subclasses decide which class to instantiate.
The benefits of FMP become clearer when you know you want an object, but you do not know how or why you want to construct that object or even what parameters you want to pass to that object. FMP allows you to defer those decisions until runtime. Not only does this reduce duplicated code, but it is also efficient, readable, and polymorphic. Having a factory, often referred to as a wrapper, creates an instance of an object means that we can dynamically swap what object we are creating based on the given circumstance.
The Deets
Looking at the unified model language (UML) we can see more easily see FMP in action. We have two thing types of things to keep track of: objects and creators. Objects are the things that are manipulated, and the creators create those objects.
The Scenario
To put this into more concrete terms let’s build a car manufacturing application. Version 1.0 is limited and only builds one type of car, the Honda Accord. You publish your app on Reddit and it immediately goes viral. Each day hundreds of users flock to your website to have cars printed out of thin air for them. After a few months, hundreds of users turn into thousands and then tens of thousands. The requests start coming in for different types of cars. Users are wanting Toyota Camrys and Ford Fusions. The team decides to scale the application and allow for the creation of different types of cars. This is going to be a major overhaul of the codebase. The team will have to analyze pain points, update business logic, and account for new user input.
The Solution
Here comes FMP! Instead of the cumbersome process of adding if/else logic for what car the user wants, we will instead use a factory method design pattern. First, we will update the Honda class to depend upon an abstract base class car. We will then create new classes for Toyota and Ford that depend upon that base car class. This ensures uniformity among our products. Next, we will create factory classes for each of our cars that we will create. Each factory class will depend upon an abstract base factory class. Now, all we must do is call the factories MakeMeACar() method and wallah, a car is printed out of thin air!
using System; public class Program { public static void Main() { CarFactory factory = null; String carName = GetCarName(); switch (carName.ToLower()) { case "ford": factory = new FordFactory(50000); break; case "toyota": factory = new ToyotaFactory( 50000); break; case "honda": factory = new HondaFactory(50000); break; default: Console.WriteLine("We don't make those cars."); break; } if (factory != null) { Car newCar = factory.MakeMeACar(); Console.WriteLine($"Your new car costs ${newCar.Price}"); } else { Console.WriteLine("We don't make those cars"); } } private static bool IsValidCar(string carName) { return carName.ToLower() == "ford" || carName.ToLower() == "toyota" || carName.ToLower() == "honda"; } private static string GetCarName() { Console.WriteLine("Enter the kind of car you want: "); var carName = Console.ReadLine(); if (IsValidCar(carName)) { Console.WriteLine($"The car you choose was a {carName}"); return carName; } return GetCarName(); } } /// <summary> /// The base abstract car class. /// Here we define what Cars should have. /// </summary> public abstract class Car { public abstract string Make { get; } public abstract Decimal Price { get; set; } } /// <summary> /// The 'Creator' Abstract Class. Here we define what creators should do. /// </summary> public abstract class CarFactory { public abstract Car MakeMeACar(); } /// <summary> /// The FordFactory is a Concrete Creator. It directly implements CarFactory. /// </summary> public class FordFactory : CarFactory { private readonly decimal _price; public FordFactory(decimal price) { _price = price; } public override Car MakeMeACar() { return new Ford(_price); } } /// <summary> /// The HondaFactory is a Concrete Creator. It creates a specific type of car. /// </summary> public class HondaFactory : CarFactory { private readonly decimal _price; public HondaFactory(decimal price) { _price = price; } public override Car MakeMeACar() { return new Honda(_price); } } /// <summary> /// The ToyotaFactory is a Concrete Creator. It creates a specific type of car. /// </summary> public class ToyotaFactory : CarFactory { private readonly decimal _price; public ToyotaFactory( decimal price) { _price = price; } public override Car MakeMeACar() { return new Toyota( _price); } } /// <summary> /// The Ford class implements the abstract class car. /// It is a concrete, specific object. /// </summary> public class Ford : Car { private decimal _price; public Ford(decimal price) { Make = "Ford"; _price = price; } public override string Make { get; } public override decimal Price { get => _price; set => _price = value; } } /// <summary> /// The Honda class implements the abstract class car. /// It is a concrete, specific object. /// </summary> public class Honda : Car { private decimal _price; public Honda( decimal price) { Make = "Honda"; _price = price; } public override string Make { get; } public override decimal Price { get => _price; set => _price = value; } } /// <summary> /// The Toyota class implements the abstract class car. /// It is a concrete, specific object. /// </summary> public class Toyota : Car { private decimal _price; public Toyota( decimal price) { Make = "Toyota"; _price = price; } public override string Make { get; } public override decimal Price { get => _price; set => _price = value; } }
To the less seasoned eye, this may seem like a useless change. With only two cars that we care about, why bother abstracting this. Consider a scenario where our growth is sustained and in 2 months, we add 3 more cars to our lineup. Instead of repeating a lot of this work, we can simply update the factory method! Our app will scale much smoother.
The Landing
Factories! With this new understanding, you should be better prepared to join a team working at scale or starting a new project that will grow rapidly. Go off into the world and create some factories.
Thanks to Christopher Okhravi for the example.