Design Patterns and Steps to Implement Singleton Class in C#

Introduction

In this article, we learn about Design Patterns, the evolution of Design Patterns, and the three types of design patterns. It is important to know about the interfaces, abstract classes, delegates, and other features related to the Oops concept, along with the design pattern. For understanding design patterns, It is very important to have knowledge about the below object-oriented concepts.

  • Abstraction
  • Inheritance
  • Polymorphism
  • Encapsulation
  • Interfaces
  • Classes
  • Abstract classes

Also, we learned about the Singleton Design Pattern and how to implement a Singleton class with a sample application using C#.

Description

In this article, I will show the below points in detail.

  • Know about Design Patterns
  • History of Design Patterns
  • Categories of Design Patterns
  • Know about Singleton Design Pattern
  • Singleton as Creational Pattern
  • Implementation Guidelines of Singleton Design Pattern
  • Steps to implement a Singleton class

Benefits of Design Pattern

Design patterns are part of object-oriented programming. After getting the design patterns, It is very easy to implement and build project architecture in real-time scenarios. The design patterns make the applications Reliable, Scalable, and easily Maintainable.

Know about Design Patterns

  • Design patterns are reusable for fixing the issues that we encounter in the day-to-day working in application programming.
  • It is generally built to fix the issues of object generation and integration processes.
  • These are templates that can be implemented for real-time programming problems.

History of Design Patterns

  • Gang of Four introduced the concepts of design patterns in their book Elements of Reusable Object-Oriented Software.
  • Gang of Four has classified the book into two categories
  • The first category describes the pros and cons of the Oops concept, and the second category explains the evolution of 23 classic software design patterns.

Categories of Design Patterns

The Design Patterns are classified into 3 categories by Gang of Four based on different issues encountered in the real-time applications, as shown below,

  1. Creational design patterns
  2. Structural design patterns
  3. Behavioral design patterns

Creational design patterns

  • It deals with object creation and initialization process
  • It gives the program more flexibility in planning which objects need to be created for a given scenario
  • Examples: Singleton, Factory Abstract Factory, etc

Structural design patterns

  • It deals with the class and object composition process
  • It focuses on the implementation of classes and their objects and the decoupling interface
  • Examples: Adapter, Facade, Bridge, etc

Behavioral design patterns

  • It deals with communication between Classes and objects process
  • Examples: Chain of Responsibility, Command and Interpreter, etc

Know about Singleton Design Pattern

  • It belongs to the Creational type pattern
  • The creational design type deals with the object-creation process
  • It explains the creation of objects in a manner which is suitable to a given scenario
  • Singleton is one of the creational design type categories (and the others are Factory, Abstract Factory, Builder, and Prototype patterns)
  • It is used when we need to ensure that only one object of a particular class is Instantiated
  • That single instance built is responsible for helping actions across the application

Singleton as Creational Pattern

Let's understand with an example,

If there are 4 objects called ClassObj1, ClassObj2, ClassObj3 and ClassObj4. These different objects try to invoke an object instantiated as a singleton called Singleton Obj, and That single instance of the object helps us to invoke underneath methods or events.

Singleton Object

Advantages of Singleton

  • Singleton controls the concurrent access to the resource
  • It ensures that only 1 object is available in the controlled state across the application

Implementation Guidelines of Singleton Design Pattern

1. Ensure that only one instance of the class exists

2. Provide global access to this instance.

  • Declaring all constructors of the class to be private.
  • Providing a static method that returns a reference to the instance.
  • The instance is stored as a private static variable.
  • Ensure that only one instance of the class exists by declaring all constructors of the class to be private.
  • To manage the singleton access, We need to provide a static property that returns a single instance of the object.

Steps to implement a Singleton class

Here, I have created a Console Application using C#, as shown below.

Console application

Then, I added a new class name called SingletonImp.cs.

Code Ref

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SingletonDesignPattern
{
    class SingletonImp
    {
        public void PrintDetails(string message)
        {
            Console.WriteLine(message);
        }
    }
}

Code Description

Here I have created a method called PrintDetails() with a parameter called message and displayed it. As of now, we have not implemented any singleton design principle in this class.

Let's see why we need Singleton class and how we apply it.

Here, we need to invoke the public method called PrintDetails() in the Program.cs file.

Code Ref:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SingletonDesignPattern
{
    class Program
    {
        static void Main(string[] args)
        {
            SingletonImp s1 = new SingletonImp();
            s1.PrintDetails("My Name is Satyaprakash");
            s1.PrintDetails("This is Singleton design");
            Console.ReadLine();
        }
    }
}

Code Description

We can use the class called SingletonImp.cs to create its object and access its method with a message parameter. Then, compile and see the result.

Output

Object class

Now, Imagine a situation where we need to invoke PrintDetails() from different classes of a given project. So, what we need to do is create multiple objects of this SingletonImp class. Let's say we create the singleton object for an employee class or a student class.

Change the Program.cs code, as shown below.

class Program
    {
        static void Main(string[] args)
        {
            // Assume we invoke the singleton object from emplyee class
            SingletonImp fromemp = new SingletonImp();
            fromemp.PrintDetails("From Employee Class");

            // Assume we invoke the singleton object from student class
            SingletonImp fromstu = new SingletonImp();
            fromstu.PrintDetails("From Srtudent Class");
            Console.ReadLine();
        }
    }

Output

Console application

So, whenever we create an instance of singleton or invoke the PrintDetails() method of singleton, we end up creating multiple objects. Let's do that. It has created multiple objects. Let's make some changes in the SingletonImp class.

Code Ref

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SingletonDesignPattern
{
    class SingletonImp
    {
        //Let's create a private static incremental variable.
        private static int counter = 0;
        
        //create a constructor
        //we will be incrementing the counter as and when a new instance created using the singleton class
        //now once we increment the counter then display the counter value.
        public SingletonImp()
        {
            counter++;
            Console.WriteLine("Counter value " + counter.ToString());
        }
     
        //Public method which can be invoked through the singleton instance 
        public void PrintDetails(string message)
        {
            Console.WriteLine(message);
        }
    }
}

Output

Console application

Here, we can see that we ended up creating the two instances of the singleton class, which is evident from the counter value two that means first and then we create the singleton object from employee the counter value is incremented to 1 and second time when we create another instance of singleton object the counter value is incremented to 2 even though we are invoking a very common method. So, we ended up creating two instances of the same singleton class as objects. Now, we proved that it's not necessary to create a new instance of an object for common methods like PrintDetails(). Let's see how we can avoid that.

So, the first thing that comes to mind is to restrict the multiple instance creation. We can achieve it by making all constructors private and changing the class to a sealed class so that it is not inherited anymore.

Let's make some changes in the SingletonImp class.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SingletonDesignPattern
{
    // change the class to sealed class
    public sealed class SingletonImp
    {
        //Let's create a private static incremental variable.
        private static int counter = 0;
        
        //create a constructor
        //we will be incrementing the counter as and when a new instance created using the singleton class
        //now once we increment the counter then display the counter value.

        //Changed the public constructor to private
        private SingletonImp()
        {
            counter++;
            Console.WriteLine("Counter value " + counter.ToString());
        }
     
        //Public method which can be invoked through the singleton instance 
        public void PrintDetails(string message)
        {
            Console.WriteLine(message);
        }
    }
}

Let's compile and see the output.

Output

Here, we are getting an error, as shown below.

Console application

The SingletonImp.cs class is inaccessible due to its protection level.

So, when we open the Program.cs file, we can see the area where the above error message is thrown.

Program.cs

So, for every object instantiation, it throws an error. Because we have changed the public contractor to private. So, we are not able to instantiate the class.

There is another way to give back the singleton object creation.

Let's see how to create a single instance of the Singleton class.

Code Ref

namespace SingletonDesignPattern
{
    // change the class to sealed class
    public sealed class SingletonImp
    {
        //Let's create a private static incremental variable.
        private static int counter = 0;
        public static SingletonImp GetInstance
        {
            get
            {
                return new SingletonImp();
            }
        }

Here, we have not overcome the problem of creating multiple instances if we return a new singleton instance in this get instance property. How do we avoid this?

Let's change the code in the SingletonImp.cs file.

Code Ref

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SingletonDesignPattern
{
    // change the class to sealed class
    public sealed class SingletonImp
    {
        //Let's create a private static incremental variable.
        private static int counter = 0;

        private static SingletonImp instance = null;
        public static SingletonImp GetInstance
        {
            get
            {
                if (instance == null)
                    instance = new SingletonImp();
                return instance;
            }
        }

        //create a constructor
        //we will be incrementing the counter as and when a new instance created using the singleton class
        //now once we increment the counter then display the counter value.

        //Changed the public constructor to private
        private SingletonImp()
        {
            counter++;
            Console.WriteLine("Counter value " + counter.ToString());
        }
     
        //Public method which can be invoked through the singleton instance 
        public void PrintDetails(string message)
        {
            Console.WriteLine(message);
        }
    }
}

With this code, we have applied the singleton design pattern to our singleton class.

Let's change the code Program.cs file.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SingletonDesignPattern
{
    class Program
    {
        static void Main(string[] args)
        {
            //Assume we invoke the singleton object from emplyee class
            SingletonImp fromemp = SingletonImp.GetInstance;
            fromemp.PrintDetails("From Employee Class");

            //Assume we invoke the singleton object from student class
            SingletonImp fromstu = SingletonImp.GetInstance;
            fromstu.PrintDetails("From Srtudent Class");
            Console.ReadLine();
        }
    }
}

Here, instead of doing a new instance of singleton class, we are getting it from SingletonImp.GetInstance property. Instead of creating a new instance, we have successfully achieved the same behavior through the GetInstance property. This GetInstance property will take care of creating one instance of the object across the application.

Let's run the application and see the result.

Output

Program.cs

Here, we can see the counter value has not been incremented, and we have got input from both employees and students as well. This means we have successfully achieved the singleton design pattern with this example.

In SingletonImp.cs file: Based on a given situation, we have successfully changed the object creation pattern of this class by applying the singleton design principles, and we have got the initial base version of the singleton design pattern. This code works perfectly well in a situation when a single thread is trying to create the instance of this class.

Complete code with comment description

Program.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SingletonDesignPattern
{
    class Program
    {
        static void Main(string[] args)
        {
//Assuming Singleton is created from employee class we refer to the GetInstance property from the SingletonImp class
            SingletonImp fromemp = SingletonImp.GetInstance;
            fromemp.PrintDetails("From Employee Class");

//Assuming Singleton is created from student class we refer to the GetInstance property from the SingletonImp class
            SingletonImp fromstu = SingletonImp.GetInstance;
            fromstu.PrintDetails("From Srtudent Class");
            Console.ReadLine();
        }
    }
}

SingletonImp.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SingletonDesignPattern
{
//Sealed ensures the class being inherited and object instantiation is restricted in the derived class
    public sealed class SingletonImp
    {
        private static int counter = 0;
//Private property initilized with null and ensures that only one instance of the object is created based on the null condition

        private static SingletonImp instance = null;
//public property is used to return only one instance of the class leveraging on the private property
        public static SingletonImp GetInstance
        {
            get
            {
                if (instance == null)
                    instance = new SingletonImp();
                return instance;
            }
        }

//Private constructor ensures that object is not instantiated other than with in the class itself
        private SingletonImp()
        {
            counter++;
            Console.WriteLine("Counter value " + counter.ToString());
        }

//Public method which can be invoked through the singleton instance
        public void PrintDetails(string message)
        {
            Console.WriteLine(message);
        }
    }
}

Summary

In this write-up, we have learned the details below.

  • Know about Design Patterns
  • History of Design Patterns
  • Categories of Design Patterns
  • Know about Singleton Design Pattern
  • Singleton as Creational Pattern
  • Implementation Guidelines of Singleton Design Pattern
  • Steps to implement a Singleton class

Thank You & Stay Tuned For More


Similar Articles