June 30, 2012

Singleton pattern

I'm sorry for pause in writing new posts about design patterns.  
Unfortunately, I had no free time, but now I'm really ready to do it.

So, today I'm going to write a post about the most popular and well-known design pattern - Singleton.

The essence of Singleton is to provide :
  • exactly one instance of class across the system;
  • simple access to it.
The implementation of Singleton based on creation a class with a method(or property in .NET) that creates an instance of this class if one doesn't exists yet. The constructor of class must be private to prevent other ways of initialization. Also Singleton must be carefully used in multi-threaded applications because in one point of time, the two threads may create two different instances (which violates singleton pattern).

Below I'll explore the most frequent implementations of pattern. As always, I'll show structural code and real-world code.



Let's start from not thread-safe implementation.


class Singleton
{
 private static Singleton instance = null;
 public static Singleton Instance
 {
  get 
  {
   if (instance == null)
    instance = new Singleton();

   return instance;
  }
 }

 private Singleton() { }
}

This is basic implementation and not thread-safe. The two different threads can evaluated the condition
if (instance == null) 
to true and create two different instances of Singleton class. But if you're creating a single-threaded application then this approach will also suitable.


Simple thread-safe implementation is situated below.

class Singleton
{
 private static object lockObject = new object();

 private static Singleton instance = null;
 public static Singleton Instance
 {
  get 
  {
   lock (lockObject)
   {
    if (instance == null)
     instance = new Singleton();

    return instance;
   }
  }
 }

 private Singleton() { }

}

This implementation is thread-safe. Here I used shared object (lockObject) to mark a statement block as critical section via lock keyword. But performance suffers because lock is acquired every time the instance requested.

Let's look at thread-safe without using locks implementation.

class Singleton
{
 private static Singleton instance = new Singleton();
 public static Singleton Instance
 {
  get 
  {
   return instance;
  }
 }

 static Singleton()
 {
 }

 private Singleton() { }
}

In C# static constructors executed (once per AppDomain) only when static member is referenced or an instance of class is created. So here you can see some sort of lazy instantiation.

Let's go toward real-world example.
I didn't want to show you example with log system as almost all books/blogs did it. So I have decided to dive deeper in restaurant's stuff.


Imagine that you're going to restaurant. So you're calling to restaurant to make a reservation. Also you want to reserve table near fountain and near big window. Hostess checking all available tables which satisfies your desires and proposing theirs to you.


So all hostesses must use ONLY ONE a piece of paper, which illustrated all tables in the restaurant and theirs statuses, or any application, which helps with it.
Singleton is really suitable for this functionality.



Restaurant and Table classes:

enum TableLocations
{
 Standard = 0,
 NearWindow = 1,
 NearPiano = 2,
 NearFountain = 4,
 NearBar = 8
}

static class Restaurant
{
 private static List<Table> tables;
 public static List<Table> Tables
 {
  get { return tables; }
  set { tables = value; }
 }

 static Restaurant()
 {
  tables = new List<Table>();

  tables.Add(new Table(101, 2) { IsAvailable = true, Location = TableLocations.NearPiano | TableLocations.NearFountain });
  tables.Add(new Table(234, 2) { IsAvailable = true, Location = TableLocations.NearWindow | TableLocations.NearFountain | TableLocations.NearPiano });
  tables.Add(new Table(341, 3) { IsAvailable = false, Location = TableLocations.Standard });
  tables.Add(new Table(11, 5) { IsAvailable = true,Location = TableLocations.NearWindow });
  tables.Add(new Table(187, 8) { IsAvailable = true, Location = TableLocations.NearPiano | TableLocations.NearBar });
 }
}

class Table
{
 public int ID { get; private set; }

 public int Roominess { get; private set; } // how many chairs can be around table?

 public bool IsAvailable { get; set; }

 public TableLocations Location { get; set; }

 public Table(int id,int tableRoominess)
 {
  this.ID = id;
  this.Roominess = tableRoominess;
 }
}
}

And here's implementation of singleton pattern - Hostess class:

class Hostess
{
 private static Hostess instance = new Hostess();
 public static Hostess Instance
 {
  get { return instance; }
 }

 static Hostess() { }

 private Hostess()
 {
 }

 public bool IsTableAvailable(int amountOfPersons)
 {
  return (from t in Restaurant.Tables
    where t.Roominess == amountOfPersons && t.IsAvailable
    select t).Any();
 }

 public bool IsTableAvailable(int amountOfPersons, TableLocations desirableLocation)
 {
  return (from t in Restaurant.Tables
    where t.Roominess == amountOfPersons && t.IsAvailable && t.Location == desirableLocation
    select t).Any();
 }
}

Usage :
class Program
{
 static void Main(string[] args)
 {
  Console.WriteLine("Is restaurant has table for 2 persons? {0}", Hostess.Instance.IsTableAvailable(2) ? "Yes" : "No");

  Console.WriteLine("Is restaurant has table for 5 persons near window? {0}", Hostess.Instance.IsTableAvailable(5, TableLocations.NearWindow) ? "Yes" : "No");

  Console.WriteLine("Is restaurant has table for 2 persons near window and fountain? {0}", Hostess.Instance.IsTableAvailable(2, TableLocations.NearWindow | TableLocations.NearFountain) ? "Yes" : "No");
 }
}

Source code of Singleton pattern you always can find on github.

Have a nice day ;)

P.S. for more details about Singleton pattern go here.

1 comment:

  1. Після тривалого процесу співбесіди з іпотечними кредиторами ми в кінцевому підсумку запропонували пана Педро Джерома про позику в його пропозиції іпотечного кредиту. Як перший покупець житла, він чудово допоміг нам отримати іпотеку, надавши чудові поради та завжди піклуючись про наші інтереси. Я б безумовно рекомендував кожному, хто купує будинок або шукає кредитора з позитивною позикою для фінансування своїх потреб або бізнесу, використовувати пана Педро Джерома та його команду для будь-якого виду позики. Я обов’язково скористаюся ними знову в майбутньому. Ви можете зв’язатися з кредитним офіцером Педро Джеромом за електронною поштою: pedroloanss@gmail.com або через WhatsApp: WhatsApp: +1-8632310632 Удачі.

    ReplyDelete