Abstract Factory

Hello everyone, and welcome to .NET Core Central. In this blog post, I am going to walk through the Abstract Factory design pattern. Abstract factory pattern is a very good pattern to discuss after we discussed the factory method design pattern in my previous blog.

The Abstract Factory design pattern is also a creational design pattern just like the factory method. And it is also part of the Gang of Four design patterns.

The main intent of the abstract factory design pattern is to allow abstraction over other factory classes. So it is kind of another layer of abstraction on top of the factory method design pattern.

Problem domain for Abstract Factory

In my previous blog post, we solved the creation of a product catalog based on the type, which is coming from the caller of the service through the factory method design pattern.

The problem domain that we are going to discuss is the same as the last blog post. It is about an e-commerce website for buying lawnmowers. Where the users are going to select a type of lawnmower and based on the type, the website will provide an appropriate lawnmower catalog.

As I mentioned just right now, the abstract factory design pattern is yet another abstraction on top of the factory method design pattern itself.

For our scenario where we have an e-commerce website for lawnmowers and we are showing the catalog. So far we only considered the scenario of type of the lawnmower. Now let us consider if we want to support the manufacturer as well.

So in the user interface of this particular application, apart from the type of the lawnmower, the user now can also select which manufacturer is manufacturing the lawnmower.

in that case, it would make sense to create an abstraction on top of this factory method itself.

The Implementation

I am going to reuse the code from the last blog post. So, the existing classes for ManualLawnmowerCatalog, ElectricLawnmowerCatalog and DieselLawnmowerCatalog are already available for us.

Now, let us support the company for the individual lawnmower types. Let us say that there is a lawnmower company called Xyz. Hence we will have three lawnmower catalog classes for the Xyz company.

  • First one for the Electric lawnmower
  • The second one is for the Diesel lawnmower
  • And finally one for the Electric lawnmower.
namespace FactoryMethodPattern.Demo
{
    public interface ILawnmowerCatalog
    {
        Lawnmower[] GetLawnmowers();
    }

    public class DieselLawnmowerCatalog : ILawnmowerCatalog
    {
        public Lawnmower[] GetLawnmowers()
        {
            return new[] { new Lawnmower { Name = "I am Diesel Lawnmower" } };
        }
    }

    public class XyzDieselLawnmowerCatalog : ILawnmowerCatalog
    {
        public Lawnmower[] GetLawnmowers()
        {
            return new[] { new Lawnmower { Name = "XYZ: I am Diesel Lawnmower" } };
        }
    }

    public class ElectricLawnmowerCatalog : ILawnmowerCatalog
    {
        public Lawnmower[] GetLawnmowers()
        {
            return new[] { new Lawnmower { Name = "I am Electric Lawnmower" } };
        }
    }

    public class XyzElectricLawnmowerCatalog : ILawnmowerCatalog
    {
        public Lawnmower[] GetLawnmowers()
        {
            return new[] { new Lawnmower { Name = "XYZ: I am Electric Lawnmower" } };
        }
    }

    public class ManualLawnmowerCatalog : ILawnmowerCatalog
    {
        public Lawnmower[] GetLawnmowers()
        {
            return new[] { new Lawnmower { Name = "I am Manual Lawnmower" } };
        }
    }

    public class XyzManualLawnmowerCatalog : ILawnmowerCatalog
    {
        public Lawnmower[] GetLawnmowers()
        {
            return new[] { new Lawnmower { Name = "XYZ: I am Manual Lawnmower" } };
        }
    }
}

The Abstract Factory class

We will keep the class for the factory method from the last blog.

namespace FactoryMethodPattern.Demo
{
    public interface ILawnmowerCatalogFactory
    {
        ILawnmowerCatalog CreateCatalog(string type);
    }
}
namespace FactoryMethodPattern.Demo
{
    public class LawnmowerCatalogFactory : ILawnmowerCatalogFactory
    {
        public ILawnmowerCatalog CreateCatalog(string type)
        {
            return type switch
            {
                "Diesel" => new DieselLawnmowerCatalog(),
                "Electric" => new ElectricLawnmowerCatalog(),
                _ => new ManualLawnmowerCatalog(),
            };
        }
    }
}

Now, we will implement a new class for the Xyz catalog classes.

namespace FactoryMethodPattern.Demo
{
    public class XyzLawnmowerCatalogFactory : ILawnmowerCatalogFactory
    {
        public ILawnmowerCatalog CreateCatalog(string type)
        {
            return type switch
            {
                "Diesel" => new XyzDieselLawnmowerCatalog(),
                "Electric" => new XyzElectricLawnmowerCatalog(),
                _ => new XyzManualLawnmowerCatalog(),
            };
        }
    }
}

So now we have two factory classes, one is for the general lawnmower catalog and the other one is for the Xyz lawnmower catalog. The two factors are responsible for creating the underlying object based on the type.

Now the abstract factory is an implementation on top of these two factory methods. And the abstract factory will be responsible for providing the factory itself. So for the abstract factory, we will create a new type LawnmowerCatalogAbstractFactory.

namespace FactoryMethodPattern.Demo
{
    public interface ILawnmowerCatalogAbstractFactory
    {
        ILawnmowerCatalogFactory CreateFactory(string manufacturer);
    }

    public class LawnmowerCatalogAbstractFactory : ILawnmowerCatalogAbstractFactory
    {
        public ILawnmowerCatalogFactory CreateFactory(string manufacturer)
        {
            return manufacturer switch
            {
                "Xyz" => new XyzLawnmowerCatalogFactory(),
                _ => new LawnmowerCatalogFactory()
            };
        }
    }
}

The LawnmowerCatalogAbstractFactory class has a single method CreateFactory. Which will return ILawnmowerCatalogFactory itself based on the manufacturer name. And as you can see the LawnmowerCatalogAbstractFactory is an abstraction on top of the catalog factory itself.

Update to controller

The LawnmowerCatalogController which we created as a part of the last blog, which was responsible for returning the lawnmowers based on the type will also change. Now the GET method will take the manufacturer name along with the type of lawnmower.

And the constructor also will change to take the ILawnmowerCatalogAbstractFactory as a dependency instead of the earlier ILawnmowerCatalogFactory.

using Microsoft.AspNetCore.Mvc;

// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860

namespace FactoryMethodPattern.Demo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class LawnmowerCatalogController : ControllerBase
    {
        private readonly ILawnmowerCatalogAbstractFactory lawnmowerCatalogAbstractFactory;

        public LawnmowerCatalogController(ILawnmowerCatalogAbstractFactory lawnmowerCatalogAbstractFactory)
        {
            this.lawnmowerCatalogAbstractFactory = lawnmowerCatalogAbstractFactory;
        }

        // GET api/<LawnmowerCatalogController>/5
        [HttpGet]
        public IEnumerable<Lawnmower> Get(string manufacturer, string type)
        {
            return lawnmowerCatalogAbstractFactory.CreateFactory(manufacturer).CreateCatalog(type).GetLawnmowers();
        }
    }
}

Now in the GET method, first we will call the CreateFactory method of the ILawnmowerCatalogAbstractFactory passing the manufacturer name to create an ILawnmowerCatalogFactory. And then call CreateCatalog of the ILawnmowerCatalogFactory passing the type. And finally, call the GetLawnmowers on the ILawnmowerCatalog to get the lawnmowers.

Update dependencies

In the Program class, I will add the dependency for the ILawnmowerCatalogAbstractFactory (in the Program class because of C# 10 and .NET 6, where the Startup class is not created by the template anymore).

using FactoryMethodPattern.Demo;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddSingleton<ILawnmowerCatalogAbstractFactory, LawnmowerCatalogAbstractFactory>();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

Now, if I run the application, I can test the implementation using the Swagger user interface. And I can see the response as expected if I pass manufacturer as Xyz and type as Electric.

abstract factory

Alternate Implementation of Abstract Factory

There is another alternative to using an abstract factory class in C#. And I personally prefer this alternative. And it is the same as the one I implemented in my last blog post.

In this alternative, instead of creating this abstract factory class and an interface, we can change the implementation a little bit here. And instead, we can have a lambda function.

So for this implementation, in the controller class, instead of injecting the ILawnmowerCatalogAbstractFactory , we will inject a lambda function, which will return ILawnmowerCatalogFactory based on the manufacturer name.

using Microsoft.AspNetCore.Mvc;

// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860

namespace FactoryMethodPattern.Demo.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class LawnmowerCatalogController : ControllerBase
    {
        private readonly Func<string, ILawnmowerCatalogFactory> lawnmowerCatalogAbstractFactory;

        public LawnmowerCatalogController(Func<string, ILawnmowerCatalogFactory> lawnmowerCatalogAbstractFactory)
        {
            this.lawnmowerCatalogAbstractFactory = lawnmowerCatalogAbstractFactory;
        }

        // GET api/<LawnmowerCatalogController>/5
        [HttpGet]
        public IEnumerable<Lawnmower> Get(string manufacturer, string type)
        {
            return lawnmowerCatalogAbstractFactory(manufacturer).CreateCatalog(type).GetLawnmowers();
        }
    }
}

Next, we will change the implementation in the Program class for the dependency injection. And instead of adding the ILawnmowerCatalogAbstractFactory to the dependency injection container, I will add the lambda function.

using FactoryMethodPattern.Demo;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();


builder.Services.AddSingleton<Func<string, ILawnmowerCatalogFactory>>(manufacturer => {
    return manufacturer switch
    {
        "Xyz" => new XyzLawnmowerCatalogFactory(),
        _ => new LawnmowerCatalogFactory()
    };
});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

In this implementation of the abstract factory method, the abstract factory is inside of the dependency injection container itself, rather than a separate class.

This is where usually I prefer to keep the code, but you can create a separate class and keep the implementation there.

Now if the run this application, I am going to see the exact same response as before.

Conclusion

I personally have not used the abstract factory a lot but I’m sure there are a lot of scenarios where it might come in handy like the scenario we discussed in this blog. And in those scenarios, this pattern will come very handy.

A Youtube video for this implementation is here.