This post is part of the series SOLID Wash Tunnel.

Definition

"Singleton is a creational design pattern that lets you ensure that a class has only one instance, while providing a global access point to this instance." - Refactoring Guru

Singleton is considered by many to be an anti-pattern, because it promotes the same disadvantages as global variables do. Sure they can be very handy to use, but they do break code modularity.

Singleton advantages:

  • It controls access to a shared resource in a multi-threaded environment.
  • It saves system resources when dealing with objects that are expensive to create.
  • It can be lazy initialized.

Singleton disadvantages:

  • It introduces global state, which even worst is preserved between different unrelated test cases. Which makes testing harder, and order of tests matter.
  • It reduces the potential for parallelism, because of the controled access nature of the singleton.
  • If the singleton is supposed to be serializable, you need to make sure only one instance is created during deserialization.

Nowdays the sigleton pattern is implemented via singleton lifetime registration. It is common to see the singleton pattern implemented in projects where there is no IoC in place, or in legacy systems.

UML class diagram. [source]


Implementation

A singleton implementation can be seen in the SOLIDWashTunnel.Legacy project. This project is intentionally sepparated as it simulates an external service of sort (web service, library, NuGet package etc.)

The legacy project provides the SOLIDWashTunnel with currency exchange rates information. It does so by providing a proxy which wraps the internal rate converter.

Since this "old" system does not have IoC support, and the process of authentication is time consuming, the LegacyCurrencyRateConverterProxy class is modeled as a singleton.

  • The class has been declared as sealed so it can not be inherited, ensuring that we suddenly do not have multiple instances of the singleton (via derived classes).
  • The constructor is made private so it can not be instantiated from outside the class.
  • _instance is declared as a private static field which holds the single available instance of this class.
  • _lock is instantiated as a simple object which prevents multiple threads from accessing _instance.
  • Instance is a public static property from which other code can access _instance in a thread-safe way.
public sealed class LegacyCurrencyRateConverterProxy
{
private readonly LegacyCurrencyRateConverter _converter;
private readonly List<string> _tokens;

private LegacyCurrencyRateConverterProxy()
{
_converter = new LegacyCurrencyRateConverter();
_tokens = new List<string>()
{
"solid-tunnel-00F1BDE0-AC18-452B-A628-B8FB0335DAB6"
};
}

private static readonly object _lock = new object();
private static LegacyCurrencyRateConverterProxy _instance;

public static LegacyCurrencyRateConverterProxy Instance
{
get
{
if (_instance == null)
{
lock (_lock)
{
if (_instance == null)
{
_instance = new LegacyCurrencyRateConverterProxy();
}
}
}

return _instance;
}
}

public ILegacyCurrencyRateConverter Authenticate(string token)
{
Console.WriteLine("Simulating authentication process to legacy system...");
Thread.Sleep(3000); // Simulating a time consuming authentication process to justify the singleton pattern
Console.WriteLine("Successfully authenticated!");
Console.WriteLine("\n\n\n");

var user = _tokens.FirstOrDefault(x => x == token);

if (user == null)
throw new InvalidOperationException("Invalid token provided.");

return _converter;
}
}

The other way the singleton pattern is implemented is via singleton lifetime registration into the IoC container. Examples of this can be seen in the ServiceRegistrations class that is housed in the root directory of the SOLIDWashTunnel project.

public static class ServiceRegistrations
{
public static IContainer AddWashTunnel(this IContainer container)
{
container.AddSingleton<IMemory, RandomAccessMemory>();
return container;
}
}

Principles

This part of the project has been modeled intentionally to represent a legacy system that does not take into considertion SOLID principles.


Continue the series on Command Message.

If you found this article helpful please give it a share in your favorite forums 😉.
The solution project is available at GitHub.