Skip to content
Carmel Eve By Carmel Eve Software Engineer I
Design Patterns in C# - Factory Method and Abstract Factory

I've been focusing lately on code quality, refactoring and architecture. A large part of my reading (& Pluralsight course-ing) has been based around design patterns, and how the application of these patterns can help bring your code in line with the SOLID principles. This means that code which uses these patterns is often more extensible, robust and testable. So I thought I'd write a (heavily dinosaur based) blog series, running through the main design patterns used in C#, starting (where I think makes the most sense) with the creational design patterns!

Creational design patterns are those which abstract away the creation of new objects. I.e. they are an attempt to stamp out the use of the "new" keyword, which couples classes together, and violates the Open-Closed principle.

Programming C# 10 Book, by Ian Griffiths, published by O'Reilly Media, is now available to buy.

The two that I am going to focus on in this blog are the factory method and abstract factory design patterns.

So, if we start with this SingleDinosaurNoiseProducer class which makes a dinosaur noise:

public class SingleDinosaurNoiseProducer : IDinosaurNoiseProducer
{
    public void MakeADinosaurNoise()
    {
        IDinosaur dinosaur = new TRex();
        dinosaur.MakeANoise();
    }
}

We can see that here, an instance of a TRex is just new-ed up in the body of the code, so this class will always make a TRex noise.

public class TRex : IDinosaur
{
    public void MakeANoise()
    {
        Console.WriteLine("RAWWR!");
    }
}

In order for to have a choice over which dinosaur noise to make, we need to be able to add new dinosaurs, without changing the code which decides when to make the noise. To do this, we must abstract away the creation of the dinosaur.

Factory Method

The factory method is as it states, a method. It takes the creation of objects and moves it out of the main body of the code. This means that in derived classes, this method can be overridden and therefore the behaviour can be extended. Also, when used with an interface, it abstracts away the exact implementation of the object returned.

For example, using the class TRexNoiseProducer:

public class TRexNoiseProducer : IDinosaurNoiseProducer
{
    public void MakeADinosaurNoise()
    {
        IDinosaur dinosaur = MakeADinosaur();

        dinosaur.MakeANoise();
    }

    public virtual IDinosaur MakeADinosaur()
    {
        return new TRex();
    }
}

The class now has a factory method called MakeADinosaur that returns an IDinosaur. In this class we return a TRex. So, if we call MakeADinosaurNoise on the TRexNoiseProducer class, then "RAWWR!" will be written out to the console.

However, as the MakeADinosaur method is virtual we can override this behaviour. If we have a class StegosaurusNoiseProducer, which derives from TRexNoiseProducer:

public class StegosaurusNoiseProducer : TRexNoiseProducer
{
    public override IDinosaur MakeADinosaur()
    {
        return new Stegosaurus();
    }
}

Here we are overriding the virtual MakeADinosaur method, to return Stegosaurus, which also implements the interface IDinosaur:

public class Stegosaurus : IDinosaur
{
    public void MakeANoise()
    {
        Console.WriteLine("Squeak?");
    }
}
The Introduction to Rx.NET 2nd Edition (2024) Book, by Ian Griffiths & Lee Campbell, is now available to download for FREE.

(I have to admit that it was around this point which I realised that I wasn't entirely sure what noise a stegosaurus did make, so this will have to do...) Therefore, when using the StegosaurusNoiseProducer class, then this derived class' factory method will be called, and when you call MakeADinosaurNoise, then "Squeak?" will be printed out to the command line instead.

Running the following:

public static void FactoryMethodExamples()
{
    IDinosaurNoiseProducer fm1 = new TRexNoiseProducer();
    fm1.MakeADinosaurNoise();

    IDinosaurNoiseProducer fm2 = new StegosaurusNoiseProducer();
    fm2.MakeADinosaurNoise();
}

Would first write out "RAWWR!" then "Squeak?" to the console.

All that would be needed to produce some different behaviour would be to add another noise producer class and override the MakeADinosaur method, returning a different concrete implementation of IDinosaur, with the correct behaviour.

However, here the calling code must still know which dinosaur it's going to return. We at some point have to decide which dinosaur we are going to produce, and then we are given a factory which produces that one dinosaur (In this example I've just instantiated the noise producers, but they could be passed in through dependency injection). What if we needed to be able to create a random dinosaur? What if we didn't want to know what dinosaur noise we were going to get at any different moment?

Abstract Factory

An abstract factory is similar to the factory method, but instead of a method it is an object in its own right.

Using an abstract factory, we can make a DinosaurNoiseProducer class, which looks like this:

public class DinosaurNoiseProducer : IDinosaurNoiseProducer
{
    private IDinosaurFactory dinosaurFactory;

    public DinosaurNoiseProducer(IDinosaurFactory dinosaurFactory)
    {
        this.dinosaurFactory = dinosaurFactory;
    }

    public void MakeADinosaurNoise()
    {
        IDinosaur dinosaur = this.dinosaurFactory.CreateADinosaur();

        if (dinosaur != null)
        {
            dinosaur.MakeANoise();
        }
    }
}

Its constructor takes in an IDinosaurFactory. This factory is then used to obtain an implementation of IDinosaur.

When you create the factory, you tell it which type of dinosaur it will create. This is then parsed as an enum, which looks like this:

public class DinosaurFactory : IDinosaurFactory
{
    private readonly DinosaurType dinosaurType;

    public DinosaurFactory(string dinosaurType)
    {
        Enum.TryParse(dinosaurType, true, out this.dinosaurType);
    }

    public IDinosaur CreateADinosaur()
    {
        switch (this.dinosaurType)
        {
            case DinosaurType.TRex:
                return new TRex();
            case DinosaurType.Stegosaurus:
                return new Stegosaurus();
        }

        return null;
    }
}
Azure Weekly is a summary of the week's top Microsoft Azure news from AI to Availability Zones. Keep on top of all the latest Azure developments!

So, when we run the following code:

public static void AbstractFactoryExamples(string dinosaurType)
{
    IDinosaurNoiseProducer af1 = new AbstractFactory.DinosaurNoiseProducer(new DinosaurFactory(dinosaurType));
    af1.MakeADinosaurNoise();
}

Then if we pass in "trex" then we the producer will be returned a returned a TRex, so when we call MakeADinosaurNoise, "RAWWR!" will be printed out to the console. Similarly we could pass in "stegosaurus" and a stegosaurus noise will be produced.

All that we would need to do here in order to add new dinosaurs would be to add another value to the enum, corresponding to the new IDinosaur with the desired behaviour, and add case to the factory. This can also be combined with reflection (through which you can discover the available classes) so that you don't need to add each case by hand. Here we can see that the dinosaur type could just be set in configuration which would allow you to decide on behaviour via configuration at run time. So here we have reproduced the previous behaviour, but reduced the need for a complex class hierarchy.

In order to create our random dinosaur factory we need a different sort of factory, a RandomDinosaurFactory:

public class RandomDinosaurFactory : IDinosaurFactory
{
  private Random random;

  RandomDinosaurFactory(Random random)
  {
      this.random = random;
  }

  public IDinosaur CreateADinosaur()
  {
      int randomNumber = random.Next(0, 10);

      if (randomNumber < 5)
      {
          return new TRex();
      }
      else
      {
          return new Stegosaurus();
      }
  }
}

Which would produce a random dinosaur each time you called it.

The different concrete implementations of IDinosaurFactory can be passed in via dependency injection, which makes this solution extremely flexible and customizable. Here we have altered the decision around which dinosaur noise will be produced, without having to change either the code producing the dinosaur noise, or the code which chooses when the noise will be made.

If you want to see the overall code for these examples, here's the GitHub repo! That's all for now, watch out for my next blog on design patterns in C#!

Doodle of the author surrounded by dinosaurs.

Carmel Eve

Software Engineer I

Carmel Eve

Carmel is a software engineer, LinkedIn Learning instructor and STEM ambassador.

Over the past four years she has been focused on delivering cloud-first solutions to a variety of problems. These have ranged from highly-performant serverless architectures, to web applications, to reporting and insight pipelines and data analytics engines.

In her time at endjin, she has written many blog posts covering a huge range of topics, including deconstructing Rx operators and mental well-being and managing remote working.

Carmel's first LinkedIn Learning course on how to prepare for the Az-204 exam - developing solutions for Microsoft Azure - was released in April 2021. Over the last couple of years she has also spoken at NDC, APISpecs and SQLBits. These talks covered a range of topics, from reactive big-data processing to secure Azure architectures.

She is also passionate about diversity and inclusivity in tech. She is a STEM ambassador in her local community and is taking part in a local mentorship scheme. Through this work she hopes to be a part of positive change in the industry.

Carmel won "Apprentice Engineer of the Year" at the Computing Rising Star Awards 2019.

Carmel worked at endjin from 2016 to 2021.