Friday, October 31, 2008

Singleton pettern in C#

In software engineering, the singleton pettern is a desgin pettern that is used to restrict instantiation of a class to one object.(This concept is also sometimes generalized to restrict the instance to a specific number of objects--for example, we can restrict the number of instance to five object). This is useful when exactly one object is needed to coordinate actions across the system. sometimes it is generalized to system that operate more efficiently when only one or a few objects exist. It is also considered a anti-pettern by some people, who feel that it is overused, introducing unneccessary limitation in situation where a sole instance of a class is not actually required.

OK, let's go!

UML Class Diagram


sample one:
//This structural code demonstrates the Singleton pattern
//which assures only a single instance (the singleton) of the class can be created.
namespace SingletonPetternTest
{
class Singleton
{
private static Singleton instance;

protected Singleton()
{ }

public static Singleton Instance()
{
//use "lazy initialization"
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}

class Program
{
static void Main(string[] args)
{
Singleton s1 = Singleton.Instance();
Singleton s2 = Singleton.Instance();

if (s1 == s2)
{
Console.WriteLine("objects are the same instance...");
}
}
}
}


sample two:
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.
// Singleton pattern -- Real World example

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

namespace DoFactory.GangOfFour.Singleton.RealWorld
{

// MainApp test application

class MainApp
{
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");
}

// All are the same instance -- use b1 arbitrarily
// Load balance 15 server requests
for (int i = 0; i < 15; i++)
{
Console.WriteLine(b1.Server);
}

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

// "Singleton"

class LoadBalancer
{
private static LoadBalancer instance;
private ArrayList servers = new ArrayList();

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();
}
}
}
}


sample three:
This .NET optimized code demonstrates the same code as above but uses more modern, built-in .NET features.

Here an elegant .NET specific solution is offered. 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.

// Singleton pattern -- .NET optimized

using System;
using System.Collections;

namespace DoFactory.GangOfFour.Singleton.NETOptimized
{

// MainApp test application

class MainApp
{

static void Main()
{
LoadBalancer b1 = LoadBalancer.GetLoadBalancer();
LoadBalancer b2 = LoadBalancer.GetLoadBalancer();
LoadBalancer b3 = LoadBalancer.GetLoadBalancer();
LoadBalancer b4 = LoadBalancer.GetLoadBalancer();

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

// All are the same instance -- use b1 arbitrarily
// Load balance 15 requests for a server
for (int i = 0; i < 15; i++)
{
Console.WriteLine(b1.Server);
}

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

// Singleton

sealed class LoadBalancer
{
// Static members are lazily initialized.
// .NET guarantees thread safety for static initialization
private static readonly LoadBalancer instance =
new LoadBalancer();

private ArrayList servers = new ArrayList();
private Random random = new Random();

// Note: constructor is private.
private LoadBalancer()
{
// List of available servers
servers.Add("ServerI");
servers.Add("ServerII");
servers.Add("ServerIII");
servers.Add("ServerIV");
servers.Add("ServerV");
}

public static LoadBalancer GetLoadBalancer()
{
return instance;
}

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


sample four:


using System.Threading;//
namespace SingletonPetternTest
{
public class Singleton
{
private static readonly Singleton _instance;
private int v;
//protected constructor is sufficient to prevent instantiation by using "new" keyword
protected Singleton()
{
Console.WriteLine("Singleton Instance Creating....");
this.V = 0;
Thread.Sleep(1000);//simulate a heavy creation cost
Console.WriteLine("Singleton Instance Created...");
}

//static constructor
static Singleton()
{
_instance = new Singleton();
}

public static Singleton Instance
{
get
{
return _instance;
}
}

public int V
{
get
{
return v;
}
set
{
v = value;
}
}

public void DoSomeWork()
{
Console.WriteLine("#");
lock (this)
{
V++;
}
Thread.Sleep(500);
}
}

class Program
{
//singleton with multithread
static void MultiThread()
{
Singleton instance = Singleton.Instance;
Thread t = new Thread(new ThreadStart(instance.DoSomeWork));
t.Start();
}

static void Main(string[] args)
{
int i;
for (i = 0; i < 10; i++)
{
Console.WriteLine("Do Some Work...");
Thread.Sleep(100);
}
//until now,this application has no any instance
for (i = 0; i < 300; i++)
{
MultiThread();
}

Thread.Sleep(1000); // wait for sure all threads finished
Console.WriteLine();
Console.WriteLine("V value expected: "+i+" actual value is : "+Singleton.Instance.V);
}
}
}

No comments: