As one of the original patterns outlined in the book “Design Patterns: Elements of Reusable Object-Oriented Software” by the “Gang of Four”, the factory pattern is the go to pattern for constructing new objects. Aside from the singleton pattern, it is probably the most popular. Removing the direct interaction with constructors and the ‘new’ keyword, the factory pattern allows for the dependence on interfaces as opposed to specific implementations. Also, construction concerns (including injecting dependencies) is encapsulated when using the factory pattern.
As the usage of IOC Containers has gained popularity over the years, I’ve seen the factory pattern used less and less. But even in the world of IOC containers like Microsoft.DependencyInjection, Unity, Autofac, etc. the factory pattern still has many benefits.
Let’s take a practical look at implementing the factory pattern in ASP.NET Core using the new built-in container.
Service Registration
The first step in using an IOC container is registering all interfaces and service types. There are several extension methods that are provided out of the box.
- AddTransient – Each time a transient object is requested, a new instance will be created.
- AddScoped – The same object will be used when requested within the same request.
- AddSingleton – The same object will always be used across all requests.
With each of these, our dependency is provided directly. With a factory though, we want to be able to retrieve our dependency on demand. As such, none of these extension methods will suit our needs. For registering a factory, a custom extension method can be created.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class ServiceCollectionExtensions | |
{ | |
public static void AddFactory<TService, TImplementation>(this IServiceCollection services) | |
where TService : class | |
where TImplementation : class, TService | |
{ | |
services.AddTransient<TService, TImplementation>(); | |
services.AddSingleton<Func<TService>>(x => () => x.GetService<TService>()); | |
} | |
} |
Now when configuring the container, we can call the AddFactory function to configure our factory.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Removed for brevity | |
namespace JrTech.AspNetCore.Web | |
{ | |
public class Startup | |
{ | |
// Removed for brevity | |
public void ConfigureServices(IServiceCollection services) | |
{ | |
services.AddFactory<IRandomNumberGenerator, RandomNumberGenerator>(); | |
services.AddMvc(); | |
} | |
// Removed for brevity | |
} | |
} |
Dependency Injection
Now with our container setup, we can inject Func<T>.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.AspNetCore.Mvc; | |
using System; | |
namespace JrTech.AspNetCore.Web.Controllers | |
{ | |
[Route("api/[controller]")] | |
public class ValuesController : Controller | |
{ | |
private readonly Func<IRandomNumberGenerator> _numberGeneratorFactory; | |
public ValuesController(Func<IRandomNumberGenerator> numberGeneratorFactory) | |
{ | |
_numberGeneratorFactory = numberGeneratorFactory; | |
} | |
// GET api/values | |
[HttpGet] | |
public string Get() | |
{ | |
var generator = _numberGeneratorFactory(); | |
return $"randomNumber – {generator.Get()}" ; | |
} | |
} | |
} |
This gives a few key benefits. Instantiation of our dependency is delayed allowing us to control when the object is initialized. If IDisposable is implemented, we can use the dependency within a using statement. The best part is we can do all of these things without interacting with the container directly.
Using a Factory Type
If we wanted to take this a step further, instead of using a Func as our factory, we could create an explicit factory type IFactory<T>.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public class Factory<T> : IFactory<T> | |
{ | |
private readonly Func<T> _initFunc; | |
public Factory(Func<T> initFunc) | |
{ | |
_initFunc = initFunc; | |
} | |
public T Create() | |
{ | |
return _initFunc(); | |
} | |
} |
To accommodate for the new factory interface, we just need to make some slight modifications to our extension method.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class ServiceCollectionExtensions | |
{ | |
public static void AddFactory<TService, TImplementation>(this IServiceCollection services) | |
where TService : class | |
where TImplementation : class, TService | |
{ | |
services.AddTransient<TService, TImplementation>(); | |
services.AddSingleton<Func<TService>>(x => () => x.GetService<TService>()); | |
services.AddSingleton<IFactory<TService>, Factory<TService>>(); | |
} | |
} |
Now when we inject our dependency, it is more clear what the intent is.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Microsoft.AspNetCore.Mvc; | |
namespace JrTech.AspNetCore.Web.Controllers | |
{ | |
[Route("api/[controller]")] | |
public class ValuesController : Controller | |
{ | |
private readonly IFactory<IRandomNumberGenerator> _numberGeneratorFactory; | |
public ValuesController(IFactory<IRandomNumberGenerator> numberGeneratorFactory) | |
{ | |
_numberGeneratorFactory = numberGeneratorFactory; | |
} | |
// GET api/values | |
[HttpGet] | |
public string Get() | |
{ | |
var generator = _numberGeneratorFactory.Create(); | |
return $"randomNumber – {generator.Get()}" ; | |
} | |
} | |
} |
What About Other Containers?
So now that you are convinced injecting a factory can be useful, what if you are not using the Microsoft.DependencyInjection container? Well, you might be in luck. Many containers, such as the ones listed below, support injecting factories as Func<TService> without any customizations at all.
- StructureMap(Lamar)
- AutoFac
- Ninject
Summary
Even in a world of dependency injection, the factory pattern still has its place. By injecting a factory, you get total control of the creation of your dependencies. With a few customization, ASP.NET Core will easily accommodate!