DEV Community

Cover image for Implementing factory pattern for dependency injection in .NET core
chaitanya.dev
chaitanya.dev

Posted on • Originally published at chaitanyasuvarna.wordpress.com

Implementing factory pattern for dependency injection in .NET core

ASP.NET Core supports the dependency injection (DI) software design pattern, which is a technique for achieving Inversion of Control (IoC) between classes and their dependencies.

A dependency is an object that another object depends on. If there is a hardcoded dependency in the class i.e. the object is instantiated in the class, this could create some problems. In case there is a need to replace the dependency with another object, it would require the modification of the class. It also makes it difficult to unit test the class.

By using dependency injection we move the creation and binding of the dependent objects outside of the class that depends on them. Thus, solving the problems that we face with hardcoded dependency and making the classes loosely coupled.

What is Factory Pattern?

The Factory Pattern was first introduced in the book “Design Patterns: Elements of Reusable Object-Oriented Software” by the “Gang of Four” (Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides).

The Factory Pattern is a Design Pattern which defines an interface for creating an object but lets the classes that have a dependency on the interface decide which class to instantiate. This abstracts the process of object generation so that the type of object to be instantiated can be determined at run-time by the class that want’s to instantiate the object.

When is Factory Pattern for DI required?

Factory Pattern is useful when there are multiple classes that implement an interface and there is a class that has a dependency on this interface. But this class will determine at run-time, based on user input, which type of object does it want to instantiate for that interface.

To understand this scenario further, let’s take an example of an interface called IShape.

public interface IShape
{
public void GetInputValues();
public void DisplaySurfaceArea();
public void DisplayVolume();
}
view raw IShape.cs hosted with ❤ by GitHub

And we also have two classes called Cube and Sphere that implement IShape.

public class Cube : IShape
{
public decimal Side { get; set; }
public void GetInputValues()
{
Console.WriteLine("Side : ");
Side = decimal.Parse(Console.ReadLine());
}
public void DisplaySurfaceArea()
{
Console.WriteLine("Surface Area of the Cube is :" + (6 * Side * Side));
}
public void DisplayVolume()
{
Console.WriteLine("Volume of the Cube is :" + (Side * Side * Side));
}
}
view raw Cube.cs hosted with ❤ by GitHub
public class Sphere : IShape
{
public decimal Radius { get; set; }
public void GetInputValues()
{
Console.WriteLine("Radius : ");
Radius = decimal.Parse(Console.ReadLine());
}
public void DisplaySurfaceArea()
{
Console.WriteLine("Surface Area of the sphere is :" + (4 * 3.14m * Radius * Radius));
}
public void DisplayVolume()
{
Console.WriteLine("Volume of the sphere is :" + (4 / 3 * 3.14m * Radius * Radius * Radius));
}
}
view raw Sphere.cs hosted with ❤ by GitHub

Now, imagine we have a service class called ShapeCalculationService which has a dependency on IShape. It takes the input from user to choose either Cube or Sphere and based on the input it has to instantiate the Cube or Sphere object at runtime, how would that be possible?

The service class needs to use this corresponding Shape object to Get the input and display the SurfaceArea and Volume for the shape.

This scenario where multiple classes implement an interface and the object that needs to be instantiated is decided at runtime, is where Factory Pattern comes to use.

How to implement Factory Pattern for DI?

Continuing with above example, we will try to implement factory pattern in this scenario.
To abstract the instantiation of the correct Shape object at runtime, we will create a ShapeFactory class who’s responsibility is to resolve which concrete class is required to be instantiated for a given selection by user.

public enum ShapeEnum
{
Sphere,
Cube
}
view raw ShapeEnum.cs hosted with ❤ by GitHub
public class ShapeFactory : IShapeFactory
{
private readonly IServiceProvider _serviceProvider;
public ShapeFactory(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public IShape GetShape(ShapeEnum shapeEnum)
{
switch(shapeEnum)
{
case ShapeEnum.Cube:
return (IShape)_serviceProvider.GetService(typeof(Cube));
case ShapeEnum.Sphere:
return (IShape)_serviceProvider.GetService(typeof(Sphere));
default:
throw new ArgumentOutOfRangeException(nameof(shapeEnum), shapeEnum, $"Shape of {shapeEnum} is not supported.");
}
}
}
public interface IShapeFactory
{
public IShape GetShape(ShapeEnum shapeEnum);
}
view raw ShapeFactory.cs hosted with ❤ by GitHub

Here you can see that the ShapeFactory class has a dependency on IServiceProvider so that ShapeFactory only resolves which concrete class needs to be instantiated but should ask the built-in service container of .Net core to get the instance and resolve its dependencies.

This is because we want to rely on IoC container of .Net core to resolve our dependencies and don’t want to make changes in out factory every single time a new dependency is introduced in either of Cube or Sphere classes.
This further decouples the code and makes it easier to manage.

The place where you setup the ServiceCollection for your project should look like below so that the DI of .Net core could figure out the required dependencies by either of the service and could resolve them at run time.

In my example, since this is a simple console application, this code resides in Program.cs

class Program
{
static void Main(string[] args)
{
//setup our DI
var serviceProvider = new ServiceCollection()
.AddTransient<IShapeFactory, ShapeFactory>()
.AddTransient<IShapeCalculationService, ShapeCalculationService>()
.AddScoped<Sphere>()
.AddScoped<IShape,Sphere>(s => s.GetService<Sphere>())
.AddScoped<Cube>()
.AddScoped<IShape, Cube>(s => s.GetService<Cube>())
.BuildServiceProvider();
//do the actual work here
var service = serviceProvider.GetService<IShapeCalculationService>();
service.CalculateShapeMeasurements();
}
}
view raw Program.cs hosted with ❤ by GitHub

Finally, I’d also like you to see the actual ShapeCalculationService that takes an input from the user and uses ShapeFactory to get the Shape class at runtime which is the used to display the surface area and the volume.

public class ShapeCalculationService : IShapeCalculationService
{
private readonly IShapeFactory _shapeFactory;
private IShape _shape;
public ShapeCalculationService(IShapeFactory shapeFactory)
{
_shapeFactory = shapeFactory;
}
public void CalculateShapeMeasurements()
{
_shape = GetShapeFromUser();
_shape.GetInputValues();
_shape.DisplaySurfaceArea();
_shape.DisplayVolume();
}
private IShape GetShapeFromUser()
{
Console.WriteLine("Enter the serial no. for the shape you want to choose :");
Console.WriteLine("1. Cube");
Console.WriteLine("2. Sphere");
var serialNumber = int.Parse(Console.ReadLine());
switch (serialNumber)
{
case 1:
return _shapeFactory.GetShape(ShapeEnum.Cube);
case 2:
return _shapeFactory.GetShape(ShapeEnum.Sphere);
default:
throw new ArgumentOutOfRangeException("Invalid input.");
}
}
}
public interface IShapeCalculationService
{
public void CalculateShapeMeasurements();
}

Conclusion

Thus we have seen how we can inject a factory to get total control of creation of our dependencies at runtime, while still using .Net core’s IoC container to resolve our dependencies.

If you are like me, who needs a small yet complete demo solution to clearly understand how this works, I have created a demo project in my github repository which would help you understand this better.
You can have a look at it here : DotNetCoreFactoryPattern

I hope you found this interesting. Thanks for reading!

Heroku

This site is built on Heroku

Join the ranks of developers at Salesforce, Airbase, DEV, and more who deploy their mission critical applications on Heroku. Sign up today and launch your first app!

Get Started

Top comments (1)

Collapse
 
eltonpering profile image
Elton A. Pering

Hey man ! good explanation. thank you !

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

AWS Security LIVE!

Hosted by security experts, AWS Security LIVE! showcases AWS Partners tackling real-world security challenges. Join live and get your security questions answered.

Tune in to the full event

DEV is partnering to bring live events to the community. Join us or dismiss this billboard if you're not interested. ❤️