SJCNet

SJCNet is the home of architect/developer/techie, Simon Coope.

Factory Pattern

This post is a continuation of the design pattern posts I've been working through recently, which can be found in GitHub.

Additionally, I would also recommend reading the Head First Design Patterns book from O'Reilly. This book really makes design patterns stick in your head and helps you to understand when one pattern is of benefit over another and why.

Overview

Factory patterns are all about deferring instantiation of concrete classes so that your implementation isn't coupled to concrete instances. Basically put, factory patterns are responsible for creating objects.

There are three main approaches to implementing a factory:

  • Simple Factory
  • Factory Method Pattern
  • Abstract Factory Pattern

You'll note I've not referred to Simple Factory as a pattern. That's because there's some contention as to whether it can be considered a pattern or not. I also find that there's confusion between the Simple Factory and the Factory Method Pattern out in the wild. So I'll cover all of this in greater detail later in the post, but for now the differences are:

Simple Factory - The simple factory class is always called directly by the class wishing to create an object (referred to as the client). So there is a direct reference between the client and the simple factory class.

Factory Method Pattern - Here the client doesn't have a direct reference to the factory class. Instead it has an abstract reference via a reference to a subclass of the factory class. The subclass is responsible for implementing its own functionality in the abstracted creation method (the factory method). Furthermore a factory method only returns one object of the type or inherited from the type specified in the abstract factory method.

Abstract Factory Pattern - This factory pattern is used to create a family of related objects, whereas the factory method is used to create one object. To do this the abstract factory uses the factory method to create the related objects. So, the Abstract Factory Pattern could be said to be an abstract implementation of a class that consists of related Factory Methods.

Simple Factory

As previously mentioned, the Simple Factory is always a direct reference to the client. So wherever we pass a concrete class into another object and use that class to create an object we're using a Simple Factory.

In the example code we have an AutomobileFactory and an AutomobileShowroom. The AutomobileShowroom has a direct reference to the AutomobileFactory injected into the constructor. Then the AutombileShowroom calls the AutomobileFactory.CreateCar method to get a concrete instance of the Car object.

public class AutomobileShowroom
{
    readonly AutomobileFactory _factory;

    public AutomobileShowroom(AutomobileFactory factory)
    {
        _factory = factory;
    }

    public ICar OrderCar(CarTypes type)
    {
        Logger.Write($"Order placed for {type} car.");

        var car =  _factory.CreateCar(type);

        car.PerformService();
        car.PerformValet();
        car.AddFuel();

        return car;
    }
}

Now, here's the first problem with this implementation. Because we're supplying a concrete AutomobileFactory we're closely coupling the factory class and the AutomobileShowroom. Meaning that if one of them changes it's likely the other one will need to change too. We've also made it difficult to test the classes.

Therefore, the Simple Factory doesn't follow the open/closed principle and it's this reason that the Simple Factory isn't considered a design pattern.

Additionally, suppose we wanted to have a different factory depending on where the showroom is (e.g. UK, Italy, etc.) and that the creation of the car is different for each factory? To do that we would need to use an abstraction.

Factory Method Pattern

When using the Factory Method Pattern the client doesn't have a direct reference to the factory implementation. Instead it has an abstract reference. However, the Factory Method Pattern also only instantiates one type (or derived type) of object.

So to best achieve that in our example we can implement abstraction via inheritance from the AutomobileShowroom class. In this case the AutombileShowroom class is an abstract class, which is then implemented by the UKAutomobileShowroom and ItalianAutomobileShowroom classes.

public abstract class AutomobileShowroom
{
    public ICar OrderCar(CarTypes type)
    {
        Logger.Write($"Order placed for {type} car.");

        var car = CreateCar(type);

        car.PerformValet();
        car.PerformService();
        car.AddFuel();

        return car;
    }

    protected abstract ICar CreateCar(CarTypes type);
}

public class UkAutomobileShowroom : AutomobileShowroom
{
    protected override ICar CreateCar(CarTypes type)
    {
        Car car;

        switch (type)
        {
            case CarTypes.Saloon:
                car = new Car(CarTypes.Saloon, 2100, Colours.Red, 5, 5);
                break;
            case CarTypes.Hatchback:
                car = new Car(CarTypes.Hatchback, 2300, Colours.Blue, 3, 4);
                break;
            case CarTypes.MPV:
                car = new Car(CarTypes.MPV, 3000, Colours.Yellow, 5, 8);
                break;
            default:
                car = new Car(CarTypes.Coupe, 2100, Colours.Red, 3, 3);
                break;
        }

        PerformSafetyChecks(car);
        PerformCrashTests(car);

        return car;
    }

    private void PerformSafetyChecks(ICar car)
    {
        Logger.Write($"Performing safety checks for {car.Name} in factory method implementation.");
    }

    private void PerformCrashTests(ICar car)
    {
        Logger.Write($"Performing crash tests for {car.Name} in factory method implementation.");
    }
}

In the above code example, we can see that the UkAutomobileShowroom has inherited from the AutombileShowroom abstract class, and then implemented the FactoryMethod (CreateCar method).

The benefit of this approach are that we are following the Open/Closed Principle, which means we're open for extension (additional factory types) but closed for modification (of existing factory/showroom types).

This approach isn't without it's drawbacks though. Because we're not using Dependency Injection we're making it more difficult to test the classes. We are also limited to each factory instantiating one type (e.g. a car). If we wanted to create additional types (e.g. a van or a motorbike) we would have to think about using the Abstract Factory Pattern.

Abstract Factory Pattern

The Abstract Factory Pattern is used to abstract the creation of a set of related objects away from the client.

In our example we might want to handle the creation of cars, vans and lorries. So to do that we can create additional Factory Methods inside one class that groups related functionality together.

We could go down the inheritance route of abstraction (as shown in the Factory Method example), or we could go down the route of interface abstraction. In this case using interface abstraction means we can inject the dependency into the AutomobileShowroom class, which makes testing easier.

So first, we need an interface:

public interface IAutomobileFactory
{
    IMotorbike CreateMotorbike(MotorbikeTypes type);

    IVan CreateVan(VanTypes type);

    ICar CreateCar(CarTypes type);
}

Then we inject the dependency into the AutomobileShowroom class:

public class AutomobileShowroom
{
    readonly IAutomobileFactory _factory;

    public AutomobileShowroom(IAutomobileFactory factory)
    {
        _factory = factory;
    }

    // Hidden for brevity...
}

Next we create the UkAutomobileFactory abstraction based on the IAutomobileFactory interface and inject that in the orchestration code. Then we can order the automobiles of our choice!

var ukAutomobileFactory = new AbstractFactory.UkAutomobileFactory();
var automobileShowroom = new AbstractFactory.AutomobileShowroom(ukAutomobileFactory);
var ukCar = automobileShowroom.OrderCar(CarTypes.Hatchback);
var ukMotorbike = automobileShowroom.OrderMotorbike(MotorbikeTypes.Chopper);

Conclusion

This was a discussion around the different types of factory pattern that can be implemented and the benefits and drawbacks of each.

Author image
About Simon Coope
Sydney, Australia Website
Experienced developer/consultant. Loves all things development, technology, gadgets, football and running.