4 Disadvantages of Singleton Pattern and How to Fix Them


The Singleton pattern is one of the most popular and commonly used design patterns.

However, as with any other pattern, the usage of the Singleton pattern also has some disadvantages, which you should be aware of if you plan on incorporating it into your project.

There are 4 main disadvantages of the Singleton design pattern are:

  1. The global nature leads to dependency hiding.
  2. It can be difficult to unit test the code.
  3. It can lead to tightly coupled code.
  4. If the single Instance of the object becomes corrupted, the entire system is compromised.

This article discusses the disadvantages of the Singleton Design pattern, and you will see how to overcome those disadvantages in C#.

What is the Singleton design pattern?

Design patterns are the basic building blocks in programming, and we always follow one of the many design patterns during development. The Gang of Four(GoF) proposed 23 design patterns, and they are used widely.

The Singleton design pattern limits the number of objects created of a specific class to one. It is the simplest and most widely used creational design pattern.

singleton design pattern

While using the Singleton pattern, you build the class so that its constructor first checks if an instance already exists or not. If yes, the constructor does not allow the object creation unless the existing object is not dumped.

Now, you may ask, what if we need to access a singleton class object? The answer is that the Singleton class provides global/public access to a single object.

In other words, different code parts share the singleton object.

What are the disadvantages of the Singleton design pattern, and how to fix them?

Although the Singleton pattern is helpful in many cases where you want to set a limit to the instance creation, offering some advantages like lazy initialization, controlled access, etc., there are some disadvantages of this design pattern that you will see in this section.

The global nature of the Singleton design pattern leads to dependency hiding

The Singleton design pattern makes it difficult to identify the Instance in its calling class. As you pass the dependencies through constructors, the singleton object becomes directly accessible without being passed through the constructors. As the code grows, it becomes difficult to identify how different objects are related. This thing can confuse any new programmer and create ambiguities.

When creating the object of a Singleton class, you do not know its dependencies as the constructor does not have them, making it seem like there are no dependencies. However, inside, there is a dependency on another object. This way, the Singleton leads to dependency hiding.

Example:

public class AnObject
{
    private SomeOtherObject someObject = SomeOtherObject.GetInstance();

    public AnObject()
    {
        //nothing here 
    }
}

Solution:

In the code example above, you can see that the constructor does not identify the object’s dependency on another class. You can refactor this code to solve this problem as follows:

public class AnObject
{
    private SomeOtherObject someObject;

    public AnObject(SomeOtherObject objectFromOutside)
    {
        this.someObject = objectFromOutside;
    }
}

This way, the user of your code can see all the dependencies instantly without watching the source code. This approach is also known as Dependency Injection (DI).

It can be difficult to unit test code that uses the Singleton Pattern

As the Singleton only allows a single instance creation, it is difficult to unit test the code as it requires creating more instances. Furthermore, as the code relies on the global state, it makes it difficult to isolate the tests, ensuring they are repeatable. Hence, you may need to use another approach for unit testing the singleton code.

Example:

As you might know, Singletons are used a lot for database connections or for writing data to files. So, you would not want your unit tests to access those. Instead, you will try to mock out the object to perform operations in the memory so that you can verify them without having any side effects. The unit tests are supposed to be self-contained and must not have connections to the database or some other files that might cause your tests to fail.

Solution:

Unit testing the Singleton objects can seem difficult, but it’s not impossible. There are three amazing approaches that you can take to make unit testing easier with singletons:

  1. Create wrapper class and use dependency injection
  2. Use static Func property
  3. Use the Extract and override call

You can find the implementation of these approaches in this article.

It can lead to tightly coupled code that is difficult to change

A code that uses a Singleton class is difficult to change as it exposes its properties and methods through a static interface making it tightly coupled to its implementation.

Example:

The Singleton pattern does not allow you to use polymorphism to use another substitute of an object while coupling you to the exact one type of singleton object. Here is the coding example:

Suppose you have an abstract class named connections having many variants as follows:

//the abstract class
public abstract class Connections
{
    public abstract void GetConnection();
}

//concrete class 1
class Connection1 : Connections
{
    public override void GetConnection()
    {
        InitializeConnection1();
    }
}

//Concrete class 2
class Connection2 : Connections
{
    public override void GetConnection()
    {
        InitializeConnection2();
    }
}

With the above code, you can create multiple connections by making the objects of the corresponding classes. However, the Singleton pattern implies that there should be a single concrete class with no extensions, so you have only one connection object.

This approach is not convenient when you need two or more connections at the runtime.

Solution:

You can avoid tight coupling by passing all the dependencies to code through parameters and constructors. You can also use the Factory Method pattern to decouple your code from specific instances.

There is only one Instance of the object, so if it becomes corrupted, the entire system is compromised

The Singleton objects are generally used in system-level parts of your code, such as database connections or file handlers. So, if any of these system-wide blocks becomes corrupted, it will affect the entire code base, making the system compromised.

Example:

As there is only one singleton object, many threads may try to access it simultaneously, creating thread safety issues. The thread-safety issue may arise in two ways:

  1. Multiple threads trying to create objects simultaneously
  2. A thread might get a reference to the partially initialized object.

Solution:

The following code implements simple thread safety using the locking mechanism:

public sealed class SingletonPattern
{
    private static SingletonPattern instance = null;
    private static object lock_code = new object();

    //private constructor
    private SingletonPattern()
    { }

    //property to access the Instance
    public static SingletonPattern Instance
    {
        get
        {
            //Check 1
            if (instance == null)
            {
                //locking the shared object
                lock (lock_code)
                {
                    //Check 2
                    if (instance == null)
                    {
                        instance = new SingletonPattern();
                    }
                }
            }
            return instance;
        }
    }
}

In the above code, the thread locks the shared object before looking for the Instance, ensuring that all the reads occur before the object locks.

This mechanism ensures that only one thread can create and access the object simultaneously, making the Singleton class somewhat thread-safe.

Conclusion

The Singleton pattern might sound to be simple according to its definition, but it has many complexities and drawbacks if used incorrectly. It is not a bad pattern in itself. However, it can be used in-properly.

The Singleton pattern does not let the classes create more than one object. It gives the global point of access to that Instance, preventing you from replication and repeated code.

In this article, you learned about the 4 major disadvantages of the Singleton pattern, some C# code examples, and ways to overcome the disadvantages.

Recent Posts