Home Architecture Practical Approach to Singleton Design Pattern

Practical Approach to Singleton Design Pattern

by Dhanik Lal Sahni

Singleton Design Pattern is used when we want to ensure that only one object of a particular class need to be created. All other objects will refer that object to get values. This pattern create object so it falls under Creation Pattern of Gang Of Four design patterns.

Condition for Singleton Design Pattern:

Singleton Design Pattern need to be implemented where below three requirements are satisfied

  1. Controls concurrent access to a shared resource
  2. Access to the shared resource will be requested from multiple, disparate parts of the system
  3. There can only be one object of class

Some Example where we can use Singleton Design Pattern:

There are many scenerio where we can use Single Design Pattern. Belwo are some real time tasks where we should use Singleton Pattern.

  • File Logging Framework
  • Application Configuration Reader
  • Connection Pool Creation
  • Print Pooler
  • Utility class like Math

Step By Step Creation of Singleton Object

Requirement:

We need to create File Logging Framework for our application. That should be shared by complete application but we don’t want object more than one.

Step 1. First step will be to create a class which wrire log in a file which is saved in application directory.

    public class LogWriter
    {
        private string m_exePath = string.Empty;
        public void LogWrite(string logMessage)
        {
            m_exePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            try
            {
                using (StreamWriter w = File.AppendText(m_exePath + "\\" + "log.txt"))
                {
                    Log(logMessage, w);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        private void Log(string logMessage, TextWriter txtWriter)
        {
            try
            {
                txtWriter.Write("\r\nLog Entry : ");
                txtWriter.WriteLine("{0} {1} : {2}", DateTime.Now.ToShortDateString(),
                    DateTime.Now.ToShortTimeString(), logMessage);
                txtWriter.WriteLine("-------------------------------");
            }
            catch (Exception ex)
            {
                throw ex; 
            }
        }
    }

This code will work fine and it will write logs. There are two problem here, we can create multiple objects and we can inherit this in another class.

    LogWriter log1 = new LogWriter();
    log1.LogWrite("Hello1");
            
    LogWriter log2 = new LogWriter();
    log2.LogWrite("Hello2");
    Log Entry : 9/5/2017 2:15 AM : Hello1
    -------------------------------
    Log Entry : 9/5/2017 2:15 AM : Hello2
    -------------------------------

Let us resolve both issues one by one.
Step 2. Stop creation of multiple objects.

We can stop creation of object using private constructor. Now as user can not create object, we have to give object using another way. Let us give object using property Logger.

    public static LogWriter _Logger;
    private LogWriter()
    {
    }
    public static LogWriter Logger
    {
        get
        {
            if (_Logger==null)
                _Logger = new LogWriter();
            return _Logger;
        }
    }

Now we can not use LogWriter class using above object creation. We have to use this like below.

    LogWriter log1 = LogWriter.Logger; // No Need to create object. This will return Logger Object.
    log1.LogWrite("Hello1");
            
    LogWriter log2 = LogWriter.Logger;
    log2.LogWrite("Hello2");

Step 3. Stop inheriting

We can stop inheritance using sealed keyword. so class will be created like below.

    public sealed class LogWriter

Now singleton class is almost ready. We may face issue only when multiple thread of application are executed. All thread will try to access LogWriter class. It may situation that threads executing property Logger. So there is chance that two object will be created. So let us make Logger property thread safe.

    private static readonly object obj = new object();
    public static LogWriter Logger
    {
        get
        {
            if (_Logger == null)  //double lock pattern
            {
                lock (obj)
                {
                    if (_Logger == null)
                        _Logger = new LogWriter();
                }
            }
            return _Logger;
        }
    }

We have implemented double lock pattern , in case one thread is try to lock the object and other thread at same time try to create object. This pattern will stop creation of multiple object in multiple thread execution environment. So now complete code will look like below.

    public sealed class LogWriter
    {
        public static LogWriter _Logger;
        private static readonly object obj = new object();
        private LogWriter()
        {
        }
        public static LogWriter Logger
        {
            get
            {
                if (_Logger == null)
                {
                    lock (obj)
                    {
                        if (_Logger == null)
                            _Logger = new LogWriter();
                    }
                }
                return _Logger;
            }
        }
        private string m_exePath = string.Empty;
        public void LogWrite(string logMessage)
        {
            m_exePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            try
            {
                using (StreamWriter w = File.AppendText(m_exePath + "\\" + "log.txt"))
                {
                    Log(logMessage, w);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        private void Log(string logMessage, TextWriter txtWriter)
        {
            try
            {
                txtWriter.Write("\r\nLog Entry : ");
                txtWriter.WriteLine("{0} {1} : {2}", DateTime.Now.ToShortDateString(),
                    DateTime.Now.ToShortTimeString(), logMessage);
                txtWriter.WriteLine("-------------------------------");
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    }

Alternate approach For achieving same pattern

Above code can be replaced with new c# concept LazyLazy is type safe so there will be no issue if multiple thread will be executed.

Lazy loading pattern will evaluate property when that will be accessed.

    public sealed class LogWriter
    {
        public static readonly Lazy<LogWriter> _Logger=new Lazy<LogWriter>();
        private LogWriter()
        {
        }
        public static LogWriter Logger
        {
            get
            {
               return _Logger.Value;
            }
        }
        private string m_exePath = string.Empty;
        public void LogWrite(string logMessage)
        {
            m_exePath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            try
            {
                using (StreamWriter w = File.AppendText(m_exePath + "\\" + "log.txt"))
                {
                    Log(logMessage, w);
                }
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
        private void Log(string logMessage, TextWriter txtWriter)
        {
            try
            {
                txtWriter.Write("\r\nLog Entry : ");
                txtWriter.WriteLine("{0} {1} : {2}", DateTime.Now.ToShortDateString(),
                    DateTime.Now.ToShortTimeString(), logMessage);
                txtWriter.WriteLine("-------------------------------");
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    }

Which SOLID pattern Singleton Design Pattern violate

Singleton design pattern violate SRP ( Single Responsibility Principle). Singleton class creating object as well as providing functionality.

Summary

Singleton Pattern will always give only one object in every situation for particular class. This pattern is useful for Resource Contention.

You may also like

Leave a Comment