👉🏼 Click here to Join I ❤️ .NET WhatsApp Channel to get 🔔 notified about new articles and other updates.
Creational Design Pattern - Singleton

Creational Design Pattern - Singleton

Design Pattern

10 Articles

Improve

In this article, let's learn about Singleton Design Pattern in .NET.

Table of Contents

  1. Introduction
  2. Structure
  3. Thread Safety
  4. Use Cases
  5. Advantages
  6. Disadvantages
  7. Related Patterns
  8. Summary

Introduction

The singleton pattern is a design pattern that ensures only one instance of a class is created and provides a way to access it globally. It's commonly used for creating objects like loggers, where you only want one instance for the entire application.

To implement the pattern, we make the class responsible for managing its own instance by making the constructor private or protected. We create a static property or method that returns the instance of the class and make sure that it's only created the first time it's accessed (lazy instantiation). Any other methods of the class are defined as regular instance methods and accessed through the static property or method.

By using the singleton pattern, we can avoid multiple instances interfering with each other, resulting in messages being logged multiple times or not at all.

Structure

Let's apply the singleton pattern structure to our real-life logger example. This pattern structure can be used for other real-life scenarios as well.

To map our logger example to the singleton pattern, we simply need to create a class called Logger, which will be the singleton. The Logger class will have a public static property called "instance", which will contain a private backing field to store the instance value. The log method in the Logger class corresponds to what is known as a singleton operation in the pattern structure.

Code Sample - Singleton Pattern

  1. Create a Logger class and add a protected constructor to it so that clients cannot instantiate it but can subclass it.
  2. Add a static Instance property and a private backing field that must be nullable.
  3. In the getter of the Instance property, check whether it's null. If it is, instantiate and store a Logger instance. Always return that instance to ensure that only one instance of the Singleton class, Logger in this case, exists at any given time.
  4. Add a Singleton operation method, Log, for testing purposes. Every instance of the Logger that's returned when getting the Instance property has access to it.
  5. Test the Singleton pattern by attempting to create a new instance of Logger, which should fail due to the protected constructor. Then, retrieve two instances of Logger via Logger.Instance and compare them. If they are the same instance and match the Logger.Instance property, write that to the console output window.
  6. Log a few messages as an example to ensure that each call does the same thing.

Note: Be aware of the potential issue with Singleton pattern, which is that it can cause difficulty in testing and mocking, and can also introduce global state, leading to unexpected behavior.

Thread Safety

That's all what we've done here is a naïve form of lazy initialization. Lazy initialization is the principle that states that we're only going to create an instance of a class once we need it and not when it's constructed. The problem is that this code is not guaranteed to be thread-safe. Let's see how to make it thread-safe!!.

Code Sample - Thread Safe Singleton Pattern

  1. Let's Use .NET's built-in thread-safe way of dealing with lazy initialization by implementing Lazy<T>.
  2. Add a new static field of type Lazy<T> to replace the old static private Logger instance, and make it readonly.
  3. Initialize the lazyLogger, passing through a method for constructing the Logger when accessing the value property for the first time.
  4. Remove the old private static Logger instance and return the value of the lazyLogger in the property getter.
  5. Test the implementation to ensure it returns the same output as the previous implementation but in a thread-safe manner.

Note: In this case, Lazy of T is used to ensure thread-safe, on-demand initialization of the Singleton class. This way, the Logger instance is only constructed when needed, not at the time of the Singleton's construction, which can cause issues with thread safety.

That's all we need to do to apply the singleton pattern structure to our logger example. Now, we can move on to implementing this in a demo.

Demo Space

Use Cases

  • The singleton pattern is useful when there needs to be only one instance of a class that can be accessed from a well-known access point, like our logger example.
  • It's also helpful when we want the sole instance to be extended by subclassing, without modifying any client code, which is possible by using a protected constructor.
  • Subclassing enables you to configure your application with the desired instance of the class at runtime.
  • Allow multiple instances without having to modify any client code.

Overall, the singleton pattern has certain consequences, which need to be considered before implementing it in a design.

Advantages

  • Strict control over how and when clients can access it.
  • Avoids polluting the namespace with global variables.
  • Subclassing enables you to configure your application with the desired instance of the class at runtime.
  • Allow multiple instances without having to modify any client code.

Disadvantages

  • Violates the single responsibility principle by having objects control their own lifecycle. But, in modern languages, an Inversion of Control (IOC) container can handle these tasks, and the Singleton class doesn't have to manage its lifetime anymore.

Despite these points, the Singleton pattern is a useful starting point because of its simplicity. Finally, let's have a look at related patterns.

  • abstract factory pattern
  • builder pattern
  • prototype pattern
  • state pattern

Summary

In this article we learn't what is Singleton Design Pattern, which makes sure that a class has only one instance, and provides a way to access it from anywhere in the code. We can create a simple implementation by adding a static Instance property and backing field with a private or protected constructor. However, this implementation may not be thread-safe. A better approach is to use lazy initialization via Lazy of T. We covered both implementations in this article. In the next article, we will learn about the factory method pattern, which is one of the two factory patterns described by the Gang of Four.

👉🏼 Click here to Join I ❤️ .NET WhatsApp Channel to get 🔔 notified about new articles and other updates.
  • Design Pattern
  • Creational
  • Singleton