The Singleton design pattern ensures a class has only one instance and provide a global point of access to it.
A visualization of the classes and objects participating in this pattern.
The classes and objects participating in this pattern include:
LoadBalancer
)
This structural code demonstrates the Singleton pattern which assures only a single instance (the singleton) of the class can be created.
using System;
namespace Singleton.Structural
{
/// <summary>
/// Singleton Design Pattern
/// </summary>
public class Program
{
public static void Main(string[] args)
{
// 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>
public class Singleton
{
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;
}
}
}
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;
namespace Singleton.RealWorld
{
/// <summary>
/// Singleton Design Pattern
/// </summary>
public class Program
{
public static void Main(string[] args)
{
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>
public class LoadBalancer
{
static LoadBalancer instance;
List<string> servers = new List<string>();
Random random = new Random();
// Lock synchronization object
private static object locker = 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 (locker)
{
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();
}
}
}
}
The .NET optimized code demonstrates the same code as above but uses more modern,
built-in .NET features.
Here is an elegant .NET specific solution. The Singleton pattern simply uses a private
constructor and a static readonly instance variable that is lazily initialized.
Thread safety is guaranteed by the compiler.
using System;
using System.Collections.Generic;
using static System.Console;
namespace Singleton.NetOptimized
{
public class Program
{
/// <summary>
/// Singleton Design Pattern
/// </summary>
public 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)
{
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;
WriteLine("Dispatch request to: " + serverName);
}
// Wait for user
ReadKey();
}
}
/// <summary>
/// The 'Singleton' class
/// </summary>
public 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();
private readonly List<Server> servers;
private readonly 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>
public class Server
{
public string Name { get; set; }
public string IP { get; set; }
}
}