A Singleton is one of the twenty three design patterns compiled to solve specific design problems which occur recursively across the software development. The Singleton pattern is a creational pattern which deals with how to create a class instance so that it is restricted to be created only once in its entire lifecycle and only one instance is to be used across the system in the entire flow.
It restricts the instantiation of a class to only one instance. This is one of the most simplest patterns which needs to be used carefully and only in required scenarios. When used unnecessarily, singleton patterns can create additional problems and can complicate the system flow which we call as an “anti-pattern” behavior.
A Singleton class can be simply created in three steps:
- Hide the constructor of the class by marking it private.
- Define a private static variable of the class to be made singleton and store an instance of the class to return.
- Define a public static method which can return the persisted instance of the class (stored in the private static field) when invoked.
For example in C# we can mark a class which offers some functionality as a singleton based on our requirement by following the above steps.
Let’s take the example of a Http utility class that offers a http get functionality for a given URL and headers.
namespace netcore3app.Providers
{
public class Http
{
private HttpClient client = new HttpClient();
public async Task<HttpResponseMessage> GetHttp(string url, Dictionary<string, string> headers)
{
var request = new HttpRequestMessage(HttpMethod.Get, url);
foreach (var header in headers)
{
request.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
return await client.SendAsync(request);
}
}
}
Now we don’t desire the utility class to be instantiated every time we require (since it’s costly to have multiple instances of HttpClient be created). We instead look to convert this class into a Singleton so that only one instance of the utility and thereby HttpClient be rotated across the system for all Http operations.
For that we mark the class as sealed (to prevent any inheritance by extension) and have a private static field that holds an instance of itself.
namespace netcore3app.Providers.Singletons
{
// a singleton class
public sealed class Http
{
private static HttpClient client = new HttpClient();
// the single instance
// which is always returned
private static Http instance = null;
// some functionality
public async Task<HttpResponseMessage> GetHttp(string url, Dictionary<string, string> headers)
{
var request = new HttpRequestMessage(HttpMethod.Get, url);
foreach (var header in headers)
{
request.Headers.TryAddWithoutValidation(header.Key, header.Value);
}
return await client.SendAsync(request);
}
// a private constructor
private Http()
{
}
// the method that returns an instance
public static Http GetInstance()
{
if (instance == null)
{
instance = new Http();
}
return instance;
}
}
}
We then have the constructor marked private to restrict instantiation and add a new method GetInstance() that returns the instance of itself when invoked. The method further delays instantiation of the class till the point of invocation and thereby creating a Lazy Initialization effect.
Another implementation can be having a static class which manages the Http utility object for us.
namespace netcore3app.Providers.Singletons
{
// a static class is singleton by nature
public static class HttpSingleton
{
// a private field to hold the instance of the type
private static readonly Http instance = new Http();
// a public property that returns the created instance
public static Http Instance => instance;
}
}
Modern day Programming Folkfore: IoC Containers
In the modern day programming frameworks which heavily utilize the features of IoC Container frameworks such as Autofac or Ninject, marking a class as a Singleton has become easy and simplified, with the frameworks doing the heavy lifting for us. And in newer ASP framework aka the ASP.NET Core brings this feature built-in with its own container for instantiations, and so these approaches are not so required when using these frameworks.
For example, in ASP.NET Core which uses its built-in IoC container for object management, we can mark a class as a Singleton as below. This ensures that the container creates only a single object during the entire application lifetime and reuses the same object for all requesting clients.
services.AddSingleton<Http>();
This way we can create Singletons which can help reduce the number of instances created and contribute to the overall system’s performance (considerably).