Pages

Tuesday, November 12, 2013

Design Pattern - Singleton

Context

In some situations, a certain type of data needs to be available to all other objects in the application. In most cases, this type of data is also unique in the system. For example, a user interface can have only one mouse pointer that all applications must access.

Problem

How do you make an instance of an object globally available and guarantee that only one instance of the class is created?

Forces

The following force act on a system within this context and must be reconciled as you consider a solution to the problem:
  •  To ensure that only a single instance of a class can exist, you must control the instantiation process. This implies that you need to prevent other objects from creating an instance of the class by using the instantiation mechanism inherent in the programming language (for example, by using the new operator). The other part of controlling the instantiation is providing a central mechanism by which all objects can obtain a reference to the single instance.

Solution

Singleton provides a global, single instance by:
  • Making the class create a single instance of itself.
  • Allowing other objects to access this instance through a class method that returns a reference to the instance. A class method is globally accessible.
  • Declaring the class constructor as private so that no other object can create a new instance.

Resulting Context

Singleton results in the following benefits:
·         Instance control. Singleton prevents other objects from instantiating their own copies of the Singleton object, ensuring that all objects access the single instance.


Examples:

Implementation Strategy:

Singleton


using System;

public class Singleton
{
   private static Singleton instance;

   private Singleton() {}

   public static Singleton Instance
   {
      get
      {
         if (instance == null)
         {
            instance = new Singleton();
         }
         return instance;
      }
   }
}

This implementation has two main advantages:
  • Because the instance is created inside the Instance property method, the class can exercise additional functionality (for example, instantiating a subclass), even though it may introduce unwelcome dependencies.
  • The instantiation is not performed until an object asks for an instance; this approach is referred to as lazy instantiation. Lazy instantiation avoids instantiating unnecessary singletons when the application starts.
          Lazy Instantiation Example:

          Let’s say we have a field of a type that is expensive to construct

class Foo
{
    public readonly Expensive expensive = new Expensive();
    ...
}

            The problem with this code is that instansiating Foo incurs the performance cost of instansiating       
         Expensive - whether-or-not the Expensive field is ever accessed. The obvious answer is to construct the 
         instance on demand or lazily instansiate the field:

class Foo
{
    Expensive _expensive;
    public Expensive
    {
        get
        {
            if (_expensive == null) _expensive = new Expensive();
            return _expensive;
        }
    }
    ...
}

The main disadvantage of this implementation, however, is that it is not safe for multithreaded environments. If separate threads of execution enter the Instance property method at the same time, more that one instance of the Singleton object may be created. Each thread could execute the following statement and decide that a new instance has to be created:

if (instance == null)


Static Initialization

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

In this strategy, the instance is created the first time any member of the class is referenced. The common language runtime takes care of the variable initialization. The class is marked sealed to prevent derivation, which could add instances. In addition, the variable is marked readonly, which means that it can be assigned only during static initialization (which is shown here) or in a class constructor.
It addresses the two basic problems that the Singleton pattern is trying to solve: global access and instantiation control. The public static property provides a global access point to the instance. Also, because the constructor is private, the Singleton class cannot be instantiated outside of the class itself; therefore, the variable refers to the only instance that can exist in the system.
Because the Singleton instance is referenced by a private static member variable, the instantiation does not occur until the class is first referenced by a call to the Instance property. This solution therefore implements a form of the lazy instantiation property.


Multithreaded Singleton

The following implementation allows only a single thread to enter the critical area, which the lock block identifies, when no instance of Singleton has yet been created:

using System; public sealed class Singleton
{
   private static volatile Singleton instance;
   private static object syncRoot = new Object();
    private Singleton() {}
    public static Singleton Instance
   {
      get
      {
         if (instance == null)
         {            lock (syncRoot)
            {
               if (instance == null)
                  instance = new Singleton();
            }
         }
         return instance;
      }
   }
}
 
This approach ensures that only one instance is created and only when the instance is needed. Also, the variable is declared to be volatileto ensure that assignment to the instance variable completes before the instance variable can be accessed. Lastly, this approach uses asyncRoot instance to lock on, rather than locking on the type itself, to avoid deadlocks.


This double-check locking approach solves the thread concurrency problems while avoiding an exclusive lock in every call to the Instanceproperty method. It also allows you to delay instantiation until the object is first accessed. In practice, an application rarely requires this type of implementation. In most cases, the static initialization approach is sufficient.

No comments:

Post a Comment