What is Factory:
A factory is a piece of code or a block of code that allows us to create entire objects at once.
Motivation:
In some cases, it is not a good idea to create objects using constructors because they are bound to the classes and their names must match the class names, which makes them less descriptive and prevents us from really describing more by using them. Additionally, passing multiple parameters can be confusing, and if we need to expand the constructor in the future, we might have to add optional parameters, which can unintentionally cause errors.
Instead of using constructors, we can use a complete separate code block and the "Factory Design Pattern" to create objects as conveniently as possible.
Why do we require it?
In order to understand why we need a factory, let's talk about some issues with constructors using an example.
public class Coordinates
{
public double x { get; set; }
public double y { get; set; }
public Coordinates(double x, double y)
{
this.x = x;
this.y = y;
}
}
We have a class that accepts "coordinates," which in this case is acceptable. However, if we decide to accept "longitude" and "latitude" as well in the future, that will prevent us from doing certain things. Let's look at an example.
public class Coordinates
{
public double x { get; set; }
public double y { get; set; }
public Coordinates(decimal x, decimal y)
{
this.x = x;
this.y = y;
}
public double lan { get; set; }
public double lat { get; set; }
public Coordinates(double lan, double lat)
{
this.lan = lan;
this.lat = lat;
}
}
This is a problem because there is no way to allow our class to accept longitude and latitude. As we can see, C# does not allow a second constructor definition because we cannot overload constructors with the same parameter definitions.
Does this problem have a solution?
Yes, and the straightforward response is "factory." We can use the same class to make some factory methods available for creating new objects; let's take a look at how we can do that.
public class Coordinates
{
public double x { get; set; }
public double y { get; set; }
private Coordinates(double x, double y)
{
this.x = x;
this.y = y;
}
public static Coordinates CreateNormalCoordinates(double x, double y)
{
return new Coordinates(x, y);
}
}
As you can see, I changed our constructor to be private, and we are now exposing a static method to create an object of Normal Co-ordinates. Adding new meanings to that class is now much simpler; all we need to do is add a new method to initialize the co-ordinates, and that will allow us to make them accept longitude and latitude as well. Let's add that method, and the finished code will resemble this:
namespace DesignPatterns
{
public class Coordinates
{
public double x { get; set; }
public double y { get; set; }
private Coordinates(double x, double y)
{
this.x = x;
this.y = y;
}
public static Coordinates CreateNormalCoordinates(double x, double y)
{
return new Coordinates(x, y);
}
public static Coordinates CreateGeoCoordinates(double longitude, double latitude)
{
return new Coordinates(longitude, latitude);
}
public override string ToString()
{
return $"{x}:{y}";
}
}
public class FactoryPattern
{
public static void Main(string[] args)
{
var normal = Coordinates.CreateNormalCoordinates(10, 20);
var geo = Coordinates.CreateGeoCoordinates(90.20, 20.80);
Console.WriteLine(normal);
Console.WriteLine(geo);
}
}
}
Therefore, the code above is much clearer now, and we are providing better APIs for users to create complete objects, which allows us to be more precise when calling those methods because there is no longer a need to overwhelm users with numerous constructor declarations.
Async Factory:
You might be wondering: We know what a factory is, but what exactly is an async factory? And it is nothing more than the asynchronous implementation of a factory. When async code is required, such as when threads are given priority and async object creation is required to support multithreading more precisely, it is important to take this into consideration.
Before we discuss why we require this kind of factory, let's take a look at an example of an async factory.
public async Coordinates(double x, double y)
{
this.x = x;
this.y = y;
}
As a result of this limitation, we must declare an Async block of code that can create an object for us. Let's take a look at how to do that in C# since there is no way to mark the constructor as "async".
public class Coordinates
{
public double x { get; set; }
public double y { get; set; }
public Coordinates(double x, double y)
{
this.x = x;
this.y = y;
}
public static async Task<Coordinates> InitWithCurrentCoordinates()
{
//Here I am adding dummy delay, you can call 3rd party api to get
//User current longitude & latitude based on public IP lookup
await Task.Delay(1000);
return new Coordinates(10, 20);
}
public override string ToString()
{
return $"{x}:{y}";
}
}
public class FactoryPattern
{
public static void Main(string[] args)
{
var currrent = Coordinates.InitWithCurrentCoordinates();
Console.WriteLine(currrent);
}
}
Let's say we need to get users' coordinates based on a reverse IP lookup and for that we need to call a third-party API. In that case, you can't just use the constructor because API calls might take several seconds to return data, and blocking the entire thread for those many seconds is not a good idea. Therefore, as you can see in the above code, we are creating a new method called "InitWithCurrentCoordinates" which is "async” in nature and can take care of both asynchronous behavior and object creation in one go.
That concludes this blog post. In our upcoming blogs, we will discuss the various factories that can be implemented, just as we did for builder patterns in our previous blogs.
Happy Coding…!!!
Top comments (0)