Learn About Static VS Singleton

Static vs Singleton

  • Static class is implicitly abstract, meaning a static class object can neither be created nor instantiated whereas singleton pattern allows us to create a single (only one) instance of a class.
  • Static class is a stateless class whereas singleton class is state full
  • Static class instance is created whenever it is first accessed and becomes accessible throughout the application or per appdomain whereas Singleton pattern provides flexibility that allows us to control instantiation based on the conditions; for example, instance of singleton class can be created per context like single instance of ShoppingCart can be created per customer
  • Data stored in the static variable or class is shared and accessed globally throughout the application whereas singleton instance created per context is shared and accessible per context meaning data stored with in the instance created in context will not be shared or accessible from instance created in another context for example items stored in ShoppingCart will be shared and accessible among different login session of a customer but will not be accessible to another customer.
  • Static class cannot be inherited or derived from any other class whereas singleton can implement interfaces and inherit from another class.
  • Static class cannot be passed as a parameter to any method whereas instance of singleton class can be passed as a parameter to a method or constructor of another class.
  • Only static members/fields/properties can be defined within the static class whereas singleton class allows both static and non-static members/fields/properties.

We will now look at and understand the below mentioned use case implementation that demonstrates all of the above facts.

Use Case 

Let’s think about the e-commerce online web portal to which thousands of customer already have subscribed. There could be a scenario when several hundred people login at the same time out of which a few people login in to the web portal from multiple applications; i.e., web portal, mobile app or background processes then logged in customers search and add items into their shopping cart. Now think of those customers who have logged in from multiple applications and add items into the shopping cart from multiple applications.

Now the request is to provide a solution that every customer should be able to view all the items of their own private shopping cart but a specific user should be able to view all the items added in the shopping cart from different applications. 

Solution

First of all we will implement a solution to have different instances of shopping cart per customer so we need three entities in the solution as mentioned below:

  1. ShoppingCart class that represents shopping cart. This class has a private field to store items added into the shopping cart.
  2. Customer class that represents a customer.
  3. LoggedinCustomers class that stores list a list of logged in customers

Is it possible to create instance of shopping cart per customer with static class implementation? The answer is No because static class is implicitly abstract meaning Static class object can neither be created nor instantiated. .Net CLR implicitly creates one instance of static class internally whenever it is first accessed and the same instance is shared among all other objects in an application.

Now the question arises – Is it possible to create an instance of shopping cart per customer with singleton pattern implementation? The answer is YES, we can use or implement Singleton pattern to restrict one instance created per customer, meaning instance of shopping cart that will be created for customer one will be different from instance created for customer two. Then later the same instance of shopping cart can be passed to different login sessions of customer.

Let’s follow the below mentioned steps to have this implementation in place,

  1. Implement a Singleton pattern in the ShoppingCart class that ensures or restricts that only one instance of shopping cart is created.
  2. To meet our requirement of one instance per customer, add a Boolean flag to determine whether we need to create an instance of ShoppingCart or not.
  3. Declare a collection object variable of type List to store items added into the Shopping Cart.
    1. /// <summary>  
    2. /// An abstract representation of Shopping Cart.  
    3. /// This class is the implementation of singleton design pattern   
    4. /// which restricts the instantiation of a class to one object  
    5. /// </summary>  
    6. public sealed class ShoppingCart  
    7. {  
    8.     private static volatile ShoppingCart _instance; //A static variable which holds a reference to the single created instance  
    9.     private static readonly object SyncRoot = new Object();  
    10.     private readonly List<string> _itemsAdded = new List<string>();  
    11.   
    12.   
    13.     /// <summary>  
    14.     /// private and parameterless constructor that prevents other classes from instantiating it  
    15.     /// </summary>  
    16.     private ShoppingCart() { }  
    17.   
    18.     /// <summary>  
    19.     /// This method is to get the instance of ShoppingCart class   
    20.     /// </summary>  
    21.     /// <param name="bCreateInstance">This flag determines whether new instance is to be created or not</param>  
    22.     /// <returns>An instance of ShoppingCart class</returns>  
    23.     public static ShoppingCart Instance(bool bCreateInstance)  
    24.     {  
    25.         if ((_instance == null) || (bCreateInstance == true))  
    26.         {  
    27.             lock (SyncRoot)  
    28.             {  
    29.                 if ((_instance == null) || (bCreateInstance == true))  
    30.                     _instance = new ShoppingCart();  
    31.             }  
    32.         }  
    33.         return _instance;  
    34.     }  
    35.   
    36.     /// <summary>  
    37.     /// This method is to add item into the Shopping cart  
    38.     /// </summary>  
    39.     /// <param name="itemCode"></param>  
    40.     public void AddItemToShoppingCart(string itemCode)  
    41.     {  
    42.         _itemsAdded.Add(itemCode);  
    43.     }  
    44.   
    45.     /// <summary>  
    46.     /// This method is to get the list of items added into the shopping cart  
    47.     /// </summary>  
    48.     /// <returns>A list of item added into the shopping cart</returns>  
    49.     public List<string> GetShoppingCartItems()  
    50.     {  
    51.         return _itemsAdded;  
    52.     }  
  4. Now define a Customer class with a field to store an instance of ShoppingCart.
  5. Within the constructor of customer class create an instance of ShoppingCart class.
  6. Within the Customer class define a method “AddItemToShoppingCart” to add item into the ShoppingCart and method “PrintShoppingCartItem” to print all the items currently added into the shopping cart
    1. /// <summary>  
    2. /// This class is a blueprint of a customer   
    3. /// </summary>  
    4. public class Customer  
    5. {  
    6.     //A local variable to store an instance of ShoppingCart.  
    7.     private readonly ShoppingCart _shoppingCartInstance = null;  
    8.  
    9.     #region Properties  
    10.   
    11.     /// <summary>  
    12.     /// Property to hold customer name  
    13.     /// </summary>  
    14.     public string CustomerName { get; set; }  
    15.  
    16.     #endregion  
    17.  
    18.     #region Constructor  
    19.     /// <summary>  
    20.     /// Constructor to initialize Customer Identifier and Customer Name  
    21.     /// </summary>  
    22.     /// <param name="customerName">Customer Name</param>  
    23.     ///   
    24.     public Customer(string customerName)  
    25.     {  
    26.         CustomerName = customerName;  
    27.         _shoppingCartInstance = ShoppingCart.Instance(true);  
    28.     }  
    29.     #endregion  
    30.   
    31.   
    32.     /// <summary>  
    33.     /// This method is to the add item into the Shopping Cart  
    34.     /// </summary>  
    35.     /// <param name="itemCode">Item to be added into the shopping cart</param>  
    36.     public void AddItemToShoppingCart(string itemCode)  
    37.     {  
    38.         _shoppingCartInstance.AddItemToShoppingCart(itemCode);  
    39.     }  
    40.   
    41.     /// <summary>  
    42.     /// This method is to print the items currently added into the shopping cart  
    43.     /// </summary>  
    44.     public void PrintShoppingCartItem()  
    45.     {  
    46.         List<string> itemsAdded = _shoppingCartInstance.GetShoppingCartItems();  
    47.   
    48.         StringBuilder itemsAddedToShoppingCart = new StringBuilder();  
    49.         foreach (var item in itemsAdded)  
    50.         {  
    51.             if (itemsAddedToShoppingCart.Length > 0)  
    52.                 itemsAddedToShoppingCart.Append(", ");  
    53.   
    54.             itemsAddedToShoppingCart.Append(item);  
    55.         }  
    56.         Console.WriteLine("Items Added in {0}'s Cart :  {1}", CustomerName, itemsAddedToShoppingCart.ToString());  
    57.     }  
    58. }  
  7. Now define a class named LoggedinCustomers.
  8. Define a field “_customers” within the LoggedinCustomers class which contains a collection of logged in Customer objects. Defined a method named “AddCustomer” to add a customer into the collection object field. Defined a method named “” to get the list of customer objects stored in the collection object
    1. public class LoggedinCustomers  
    2. {  
    3.     private readonly List<Customer> _customers = new List<Customer>();  
    4.   
    5.     /// <summary>  
    6.     /// This method is to get the customer from loggedin customer collection based on the customer name  
    7.     /// </summary>  
    8.     /// <param name="customerName">Customer Name</param>  
    9.     /// <returns>An instance of customer</returns>  
    10.     public Customer GetCustomer(string customerName)  
    11.     {  
    12.         Customer customer = _customers.FirstOrDefault(x => x.CustomerName == customerName);  
    13.         return customer;  
    14.     }  
    15.   
    16.     /// <summary>  
    17.     /// This method is to add customer into the loggedin customer collection  
    18.     /// </summary>  
    19.     /// <param name="customer">an instance of customer class</param>  
    20.     public void AddCustomer(Customer customer)  
    21.     {  
    22.         _customers?.Add(customer);  
    23.     }  
    24.   
    25.     /// <summary>  
    26.     ///   
    27.     /// </summary>  
    28.     /// <returns></returns>  
    29.     public List<Customer> GetCustomers()  
    30.     {  
    31.         return _customers;  
    32.     }  
    33. }  

Now write a program that instantiates an instance of Customer class for different users whereas retrieves an existing instance of customer class from a list of logged in customers maintained. This way we will be able to create different instances of shopping cart per customer and add item into the shopping cart.

Code Snippet

  1. public static void StartProcess()  
  2. {  
  3.     LoggedinCustomers loggedinCustomers = new LoggedinCustomers();  
  4.     while (true)  
  5.     {  
  6.         Console.Clear();  
  7.         PrintLoggedinCustomers(loggedinCustomers);  
  8.   
  9.         Console.Write("Enter 1 to login or 0 to logout from the application: ");  
  10.         string input = Console.ReadLine();  
  11.         if (input == "1")  
  12.         {  
  13.             var currentCustomer = LoginCustomer(loggedinCustomers);  
  14.   
  15.             Console.WriteLine("{0} logged in sucessfully!! Let's Start Shopping ", currentCustomer.CustomerName);  
  16.             StartShopping(currentCustomer);  
  17.         }  
  18.         else  
  19.         {  
  20.             break;  
  21.         }  
  22.     }  
  23. }  
  24.   
  25. private static Customer LoginCustomer(LoggedinCustomers loggedinCustomers)  
  26. {  
  27.     Console.Write("Enter Customer Name to login: ");  
  28.     string customerName = Console.ReadLine();  
  29.     //search if the entered customer is already logged in then use the object   
  30.     var currentCustomer = loggedinCustomers.GetCustomer(customerName);  
  31.     //If the entered customer is not loggedin already then create a new instance   
  32.     if (null == currentCustomer)  
  33.     {  
  34.         currentCustomer = new Customer(customerName);  
  35.         loggedinCustomers.AddCustomer(currentCustomer);  
  36.     }  
  37.     return currentCustomer;  
  38. }  
  39.   
  40. private static void PrintLoggedinCustomers(LoggedinCustomers loggedinCustomers)  
  41. {  
  42.     var customers = loggedinCustomers.GetCustomers();  
  43.     if (0 >= customers.Count)  
  44.     {  
  45.         Console.WriteLine("No Customer is currently logged in to the application");  
  46.     }  
  47.     else  
  48.     {  
  49.         foreach (Customer customer in customers)  
  50.         {  
  51.             Console.WriteLine("{0} is currently logged in", customer.CustomerName);  
  52.             customer.PrintShoppingCartItem();  
  53.         }  
  54.     }  
  55. }  
  56.   
  57. private static void StartShopping(Customer customer)  
  58. {  
  59.     Console.WriteLine("Enter 0 to logout from the application");  
  60.     Console.WriteLine("Enter 1 to Print Shopping Cart Item");  
  61.     Console.WriteLine("Enter 2 to add item into the shopping cart");  
  62.     string line;  
  63.     while ((line = Console.ReadLine()) != "0")  
  64.     {  
  65.         if (line == "1")  
  66.         {  
  67.             customer.PrintShoppingCartItem();  
  68.         }  
  69.         else if (line == "2")  
  70.         {  
  71.             Console.WriteLine("Enter 9 to Stop Shopping else add Item Name to Add into the shopping cart else");  
  72.             Console.WriteLine("");  
  73.             string itemCode;  
  74.             while ((itemCode = Console.ReadLine()) != "9")  
  75.             {  
  76.                 customer.AddItemToShoppingCart(itemCode);  
  77.             }  
  78.   
  79.             Console.WriteLine("Enter 0 to logout from the application");  
  80.             Console.WriteLine("Enter 1 to Print Shopping Cart Item");  
  81.             Console.WriteLine("Enter 2 to add item into the shopping cart");  
  82.         }  
  83.     }  
  84. }  

Output

Two customers named “User1” and “User2” logged into the application. Both of them added different items into the shopping cart and we can view their shopping cart in the below image.

Output

Now we will make the changes to share the same instance for all the login session of a customer. For this we will require CustomerLoginSession class that represents a login session for a customer. A customer can login from different platforms or applications i.e., Web Application or Mobile Application

Let’s follow the below mentioned steps to have this implementation in place:

  1. Define a class named “CustomerLoginSession” to represent customer login session. This class will have a field named “_shoppingCartInstance” of type ShoppingCart to hold a reference of ShoppingCart instance created in Customer Class. Customer Class will pass an object as a parameter to the constructor of CustomerLoginSession class.
  2. Within the CustomerLoginSession class define a method “AddItemToShoppingCart” to add item into the ShoppingCart.
    1. public class CustomerLoginSession  
    2. {  
    3.     // An instance of ShoppingCart  
    4.     private readonly ShoppingCart _shoppingCartInstance = null;  
    5.     private readonly string _customerName;  
    6.     public string ApplicationId { get; set; }  
    7.   
    8.     /// <summary>  
    9.     /// Default Constructor to initialize Customer Session  
    10.     /// </summary>  
    11.     /// <param name="instance">An instance of singleton class which is passed as a parameter</param>  
    12.     /// <param name="customerLoginApplicationId">Unique Login Session Identifier</param>  
    13.     /// <param name="customerName">Customer Name</param>  
    14.     public CustomerLoginSession(ShoppingCart instance, string customerLoginApplicationId, string customerName)  
    15.     {  
    16.         _shoppingCartInstance = instance;  
    17.         ApplicationId = customerLoginApplicationId;  
    18.         _customerName = customerName;  
    19.     }  
    20.   
    21.     /// <summary>  
    22.     /// This method is to the add item into the Shopping Cart  
    23.     /// </summary>  
    24.     /// <param name="itemCode">Item to be added into the shopping cart</param>  
    25.     public void AddItemToShoppingCart(string itemCode)  
    26.     {  
    27.         _shoppingCartInstance.AddItemToShoppingCart(itemCode);  
    28.         Console.WriteLine("{0} added into {1} Cart from {2} ", itemCode, _customerName, ApplicationId);  
    29.     }  
    30. }  
  3. Define a field “_customerLoginSessions” within the Customer class which contains a collection of CustomerLoginSession objects because Customer can have multiple login sessions (1-to-many * multiplicity
    1. //A list of customer login sessions  
    2.   
    3. private readonly List<CustomerLoginSession> _customerLoginSessions = new List<CustomerLoginSession>();  
  4. Define a property named “CurrentCustomerLoginSession” of type CustomerLoginSession within the Customer class to store current login session. In real implementation we don’t require this property. I have created to keep this project simple.
    1. private CustomerLoginSession CurrentCustomerLoginSession { get; set; }  
  5. Update method “AddItemToShoppingCart” within the Customer class to make a call to the method “AddItemToShoppingCart” to CurrentCustomerLoginSession object that will further add item into the ShoppingCart.
    1. public void AddItemToShoppingCart(string itemCode)  
    2. {  
    3.     CurrentCustomerLoginSession.AddItemToShoppingCart(itemCode);  
    4. }  
  6. Add method “InitializeCustomerLoginSession” within the Customer class to instantiate CustomerLoginSession object and pass a reference of shopping cart instance created in the customer class so that each customer login session has access to the same shopping cart instance.
    1. public void InitializeCustomerLoginSession(string applicationId)  
    2. {  
    3.     CustomerLoginSession loginSession = _customerLoginSessions.FirstOrDefault(x => x.ApplicationId == applicationId);  
    4.   
    5.     if (null == loginSession)  
    6.     {  
    7.         loginSession = new CustomerLoginSession(_shoppingCartInstance, applicationId, CustomerName);  
    8.         _customerLoginSessions.Add(loginSession);  
    9.     }  
    10.   
    11.     CurrentCustomerLoginSession = loginSession;  
    12. }  

Now write a program that instantiates an instance of Customer class for different customers whereas retrieves an existing instance of customer class from a list of logged in customers maintained but initializes customer login session for different platforms. This way we will be able to create different instances of shopping cart per customer but share the same instance of shopping cart among different customer login sessions. Customers will be able to add items into the shopping cart from different session and able to see all the items added from different sessions.

  1. public static void StartProcess()  
  2.         {  
  3.             LoggedinCustomers loggedinCustomers = new LoggedinCustomers();  
  4.             while (true)  
  5.             {  
  6.                 Console.Clear();  
  7.                 PrintLoggedinCustomers(loggedinCustomers);  
  8.   
  9.                 Console.Write("Enter 1 to login or 0 to exit: ");  
  10.                 string input = Console.ReadLine();  
  11.                 if (input == "1")  
  12.                 {  
  13.                     var currentCustomer = LoginCustomer(loggedinCustomers);  
  14.                     StartShopping(currentCustomer);  
  15.                 }  
  16.                 else  
  17.                 {  
  18.                     break;  
  19.                 }  
  20.             }  
  21.         }  
  22.           
  23.         private static Customer LoginCustomer(LoggedinCustomers loggedinCustomers)  
  24.         {  
  25.             Console.Write("Enter Customer Name to login: ");  
  26.             string customerName = Console.ReadLine();  
  27.   
  28.             Console.Write("Enter Application Name Like Web Or Mobile: ");  
  29.             string application = Console.ReadLine();  
  30.   
  31.             //search if the entered customer is already logged in then use the object   
  32.             var currentCustomer = loggedinCustomers.GetCustomer(customerName);  
  33.             //If the entered customer is not loggedin already then create a new instance   
  34.             if (null == currentCustomer)  
  35.             {  
  36.                 currentCustomer = new Customer(customerName);  
  37.                 loggedinCustomers.AddCustomer(currentCustomer);  
  38.             }  
  39.   
  40.             currentCustomer.InitializeCustomerLoginSession(application);  
  41.             Console.WriteLine("{0} logged in sucessfully from {1} !! Let's Start Shopping ", currentCustomer.CustomerName, application);  
  42.   
  43.   
  44.             return currentCustomer;  
  45.         }  
  46.   
  47.         private static void StartShopping(Customer customer)  
  48.         {  
  49.             Console.WriteLine("Enter 0 to logout from the application");  
  50.             Console.WriteLine("Enter 1 to Print Shopping Cart Item");  
  51.             Console.WriteLine("Enter 2 to add item into the shopping cart");  
  52.             string line;  
  53.             while ((line = Console.ReadLine()) != "0")  
  54.             {  
  55.                 if (line == "1")  
  56.                 {  
  57.                     customer.PrintShoppingCartItem();  
  58.                 }  
  59.                 else if (line == "2")  
  60.                 {  
  61.                     Console.WriteLine("Enter 9 to Stop Shopping else add Item Name to Add into the shopping cart else");  
  62.                     Console.WriteLine("");  
  63.                     string itemCode;  
  64.                     while ((itemCode = Console.ReadLine()) != "9")  
  65.                     {  
  66.                         customer.AddItemToShoppingCart(itemCode);  
  67.                     }  
  68.   
  69.                     Console.WriteLine("Enter 0 to logout from the application");  
  70.                     Console.WriteLine("Enter 1 to Print Shopping Cart Item");  
  71.                     Console.WriteLine("Enter 2 to add item into the shopping cart");  
  72.                 }  
  73.             }  
  74.         }  
  75.           
  76.         private static void PrintLoggedinCustomers(LoggedinCustomers loggedinCustomers)  
  77.         {  
  78.             var customers = loggedinCustomers.GetCustomers();  
  79.             if (0 >= customers.Count)  
  80.             {  
  81.                 Console.WriteLine("No Customer is currently logged in to the application");  
  82.             }  
  83.             else  
  84.             {  
  85.                 foreach (Customer customer in customers)  
  86.                 {  
  87.                     List<CustomerLoginSession> loginSessions = customer.GetCustomerLoginSessions();  
  88.                     string applications = string.Join(", ", from item in loginSessions select item.ApplicationId);  
  89.                     Console.WriteLine("{0} is currently logged in from {1}", customer.CustomerName, applications);  
  90.                     customer.PrintShoppingCartItem();  
  91.                 }  
  92.             }  
  93.         }  

Output

Customer named “User 1” logged into the application from Web and Mobile application at the same time. User added Mobile, Shoes into the shopping cart from Web Application whereas user added Jeans from Mobile Application. Finally, user could view all the items added into the shopping cart irrespective of which item was added from which application or login session.

Output

Overall Class Structure

Output

Conclusion

Based on the above example, I would propose to use Singleton over Static whenever we need to create instance based on some context and use Static over Singleton whenever we need the same instance or data throughout the application.