Skip to content

Singleton

Summary: Ensure a class has only one instance and provide a global point of access to it.

Frequency of use: Medium high

UML class diagram

alt

Participants

The classes and objects participating in this pattern are:

  • Singleton   (LoadBalancer)
  • defines an Instance operation that lets clients access its unique instance. Instance is a class operation.
  • responsible for creating and maintaining its own unique instance.

Structural code in C# .{10}

This structural code demonstrates the Singleton pattern which assures only a single instance (the singleton) of the class can be created.

using System;

namespace DoFactory.GangOfFour.Singleton.Structural
{
    /// <summary>
    /// MainApp startup class for Structural
    /// Singleton Design Pattern.
    /// </summary>
    class MainApp
    {
        /// <summary>
        /// Entry point into console application.
        /// </summary>
        static void Main()
        {
            // Constructor is protected -- cannot use new
            Singleton s1 = Singleton.Instance();
            Singleton s2 = Singleton.Instance();

            // Test for same instance
            if (s1 == s2)
            {
                Console.WriteLine("Objects are the same instance");
            }

            // Wait for user
            Console.ReadKey();
        }
    }

    /// <summary>
    /// The 'Singleton' class
    /// </summary>
    class Singleton
    {
        private static Singleton _instance;

        // Constructor is 'protected'
        protected Singleton()
        {
        }

        public static Singleton Instance()
        {
            // Uses lazy initialization.
            // Note: this is not thread safe.
            if (_instance == null)
            {
                _instance = new Singleton();
            }

            return _instance;
        }
    }
}

Output

Objects are the same instance

Real-world code in C# .{10}

This real-world code demonstrates the Singleton pattern as a LoadBalancing object. Only a single instance (the singleton) of the class can be created because servers may dynamically come on- or off-line and every request must go throught the one object that has knowledge about the state of the (web) farm.

using System;
using System.Collections.Generic;
using System.Threading;

namespace DoFactory.GangOfFour.Singleton.RealWorld
{
    /// <summary>
    /// MainApp startup class for Real-World
    /// Singleton Design Pattern.
    /// </summary>
    class MainApp
    {
        /// <summary>
        /// Entry point into console application.
        /// </summary>
        static void Main()
        {
            LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
            LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
            LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
            LoadBalancer b4 = LoadBalancer.GetLoadBalancer();

            // Same instance?
            if (b1 == b2 && b2 == b3 && b3 == b4)
            {
                Console.WriteLine("Same instance\n");
            }

            // Load balance 15 server requests
            LoadBalancer balancer = LoadBalancer.GetLoadBalancer();
            for (int i = 0; i < 15; i++)
            {
                string server = balancer.Server;
                Console.WriteLine("Dispatch Request to: " + server);
            }

            // Wait for user
            Console.ReadKey();
        }
    }

    /// <summary>
    /// The 'Singleton' class
    /// </summary>
    class LoadBalancer
    {
        private static LoadBalancer _instance;
        private List<string> _servers = new List<string>();
        private Random _random = new Random();

        // Lock synchronization object
        private static object syncLock = new object();

        // Constructor (protected)
        protected LoadBalancer()
        {
            // List of available servers
            _servers.Add("ServerI");
            _servers.Add("ServerII");
            _servers.Add("ServerIII");
            _servers.Add("ServerIV");
            _servers.Add("ServerV");
        }

        public static LoadBalancer GetLoadBalancer()
        {
            // Support multithreaded applications through
            // 'Double checked locking' pattern which (once
            // the instance exists) avoids locking each
            // time the method is invoked
            if (_instance == null)
            {
                lock (syncLock)
                {
                    if (_instance == null)
                    {
                        _instance = new LoadBalancer();
                    }
                }
            }

            return _instance;
        }

        // Simple, but effective random load balancer
        public string Server
        {
            get
            {
                int r = _random.Next(_servers.Count);
                return _servers[r].ToString();
            }
        }
    }
}

Output

Same instance

ServerIII
ServerII
ServerI
ServerII
ServerI
ServerIII
ServerI
ServerIII
ServerIV
ServerII
ServerII
ServerIII
ServerIV
ServerII
ServerIV

.NET Optimized code in C# .{10}

alt

```cs{44,46-50,56-57,70-73} using System; using System.Collections.Generic;

namespace DoFactory.GangOfFour.Singleton.NETOptimized { ///

/// MainApp startup class for .NET optimized /// Singleton Design Pattern. /// class MainApp { /// /// Entry point into console application. /// static void Main() { var b1 = LoadBalancer.GetLoadBalancer(); var b2 = LoadBalancer.GetLoadBalancer(); var b3 = LoadBalancer.GetLoadBalancer(); var b4 = LoadBalancer.GetLoadBalancer();

        // Confirm these are the same instance
        if (b1 == b2 && b2 == b3 && b3 == b4)
        {
            Console.WriteLine("Same instance\n");
        }

        // Next, load balance 15 requests for a server
        var balancer = LoadBalancer.GetLoadBalancer();
        for (int i = 0; i < 15; i++)
        {
            string serverName = balancer.NextServer.Name;
            Console.WriteLine("Dispatch request to: " + serverName);
        }

        // Wait for user
        Console.ReadKey();
    }
}

/// <summary>
/// The 'Singleton' class
/// </summary>
sealed class LoadBalancer
{
    // Static members are 'eagerly initialized', that is,
    // immediately when class is loaded for the first time.
    // .NET guarantees thread safety for static initialization
    private static readonly LoadBalancer _instance =
        new LoadBalancer();

    // Type-safe generic list of servers
    private List<Server> _servers;
    private Random _random = new Random();

    // Note: constructor is 'private'
    private LoadBalancer()
    {
        // Load list of available servers
        _servers = new List<Server>
            {
              new Server{ Name = "ServerI", IP = "120.14.220.18" },
              new Server{ Name = "ServerII", IP = "120.14.220.19" },
              new Server{ Name = "ServerIII", IP = "120.14.220.20" },
              new Server{ Name = "ServerIV", IP = "120.14.220.21" },
              new Server{ Name = "ServerV", IP = "120.14.220.22" },
            };
    }

    public static LoadBalancer GetLoadBalancer()
    {
        return _instance;
    }

    // Simple, but effective load balancer
    public Server NextServer
    {
        get
        {
            int r = _random.Next(_servers.Count);
            return _servers[r];
        }
    }
}

/// <summary>
/// Represents a server machine
/// </summary>
class Server
{
    // Gets or sets server name
    public string Name { get; set; }

    // Gets or sets server IP address
    public string IP { get; set; }
}

} ```