When writing object oriented code we end up needing to create a lot of different objects. Sometimes the process to create those objects is complex or maybe we don't know exactly which type we will need to create.

Situations like these are where using the Factory pattern comes in handy.

The factory pattern allows us to isolate the object creation away from the consumers, keep complex object creation logic maintained in a single, reusable spot, & change which concrete implementation is used without impacting other parts of the code.


Real World Example

Here is an example of a factory that I used within an audio transcription service I built.

Audio comes in many different file types; .mp3, .mp4, or .wav for example. Before my service can transcribe the audio, it must be in a unified format: .wav.

Because of that restriction, we need to be able to determine which type of audio converter must be used & convert the audio before we start transcribing. One user might upload an .mp3 while others upload .wav.

This is a perfect use case for a Factory.

We can create a common interface, IAudioConverter & create a factory that can determine which concrete implementation should be used for each audio file at runtime.

Let's look at the code.

We will start by defining the IAudioConverter interface. We know the classes that implement this interface have two responsibilites. First, they need to expose a method that tells the factory if it can convert the requested audio type. Second, it needs to perform the audio conversion for the transcription service.

public interface IAudioConverter
{
    bool CanConvert(AudioFileType audioType);

    AudioFile Convert(AudioFile originalAudio);
}

Next up is our factory. The factory will keep track of all known IAudioConverters & provide a method to get the correct IAudioConverter instance based on a specific AudioFileType.

public static class AudioConverterFactory
{
    // The collection of known IAudioConverter implementations
    private readonly IEnumerable<IAudioConverter> _converters = new IAudioConverter[]
    {
        new WavToWavConverter(),
        new MpToWavConverter()
    }

    
    public static IAudioConverter GetConverter(AudioFileType audioType)
    {
        // Find the audio converter that can be used for the audioType
        // There should be a 1-to-1 relationship for AudioFileType & IAudioConverter 
        var converter = _converters
            .SingleOrDefault(converter => converter.CanConvert(audioType));

        // If we don't find one, we cannot convert the audio
        if(converter == null)
        {
            throw new UnknownAudioFileTypeException(audioType);
        } 

        // Return the converter
        return converter;
    }
}

Our factory is pretty straight forward. We also added some logic to throw an exception if the factory is unable to find an IAudioConverter for the requested AudioFileType.

Now, we will look at the transcription service that will be using the factory to retrieve a concrete IAudioConverter implementation.

public class TranscriptionService
{
    public Transcript Transcribe(AudioFile audio)
    {
        // First, convert the audio to the correct format
        IAudioConverter converter = AudioConverterFactory.GetConverter(audio.AudioFileType);
        
        AudioFile convertedAudio = converter.Convert(audio);
        
        // TODO: Transcribe converted audio...
    }
}

The transcription service only needs to know that it requires an IAudioConverter & that the AudioConverterFactory can give it one. Once it has the IAudioConverter implementation, it just has to call the Convert method passing in its audio file.

This flow remains the same for every audio file; there is no conditional logic to check the audio format. This is to simplify the flow so that as developers read the code, they do not have to keep up with branching statements. cheeze

VoilĂ ! We have ourselves an excelent solution to the problem of not knowing which implementation of IAudioConverter is needed until the audio file is provided.


Hopefully this article helps demystify the Factory pattern a bit so you can use it to keep your own code bases clean & mantainable.