Singleton


SINGLETON is a very simple pattern.[2] The test case in Listing 24-1 shows how it should work. The first test function shows that the Singleton instance is accessed through the public static method Instance and that if Instance is called multiple times, a reference to the exact same instance is returned each time. The second test case shows that the Singleton class has no public constructors, so there is no way for anyone to create an instance without using the Instance method.

[2] [GOF95], p. 127

Listing 24-1. Singleton test case

using System; using System.Reflection; using NUnit.Framework; [TestFixture] public class TestSimpleSingleton {   [Test]   public void TestCreateSingleton()   {     Singleton s = Singleton.Instance;     Singleton s2 = Singleton.Instance;     Assert.AreSame(s, s2);   }   [Test]   public void TestNoPublicConstructors()   {     Type singleton = typeof(Singleton);     ConstructorInfo[] ctrs = singleton.GetConstructors();     bool hasPublicConstructor = false;     foreach(ConstructorInfo c in ctrs)     {       if(c.IsPublic)       {         hasPublicConstructor = true;         break;       }     }     Assert.IsFalse(hasPublicConstructor);   } }

This test case is a specification for the SINGLETON pattern and leads directly to the code shown in Listing 24-2. By inspecting this code, it should be clear that there can never be more than one instance of the Singleton class within the scope of the static variable Singleton.theInstance.

Listing 24-2. Singleton implementation

public class Singleton {   private static Singleton theInstance = null;   private Singleton() {}   public static Singleton Instance   {     get     {       if (theInstance == null)         theInstance = new Singleton();       return theInstance;     }   } }

Benefits

  • Cross-platform: Using appropriate middleware (e.g., Remoting), SINGLETON can be extended to work across many CLRs (Common Language Runtime) and many computers.

  • Applicable to any class: You can change any class into a SINGLETON simply by making its constructors private and adding the appropriate static functions and variable.

  • Can be created through derivation: Given a class, you can create a subclass that is a SINGLETON.

  • Lazy evaluation: If the SINGLETON is never used, it is never created.

Costs

  • Destruction undefined: There is no good way to destroy or decommission a SINGLETON. If you add a decommission method that nulls out theInstance, other modules in the system may still be holding a reference to the SINGLETON. Subsequent calls to Instance will cause another instance to be created, causing two concurrent instances to exist. This problem is particularly acute in C++, in which the instance can be destroyed, leading to possible dereferencing of a destroyed object.

  • Not inherited: A class derived from a SINGLETON is not a SINGLETON. If it needs to be a SINGLETON, the static function and variable need to be added to it.

  • Efficiency: Each call to Instance invokes the if statement. For most of those calls, the if statement is useless.

  • Nontransparent: Users of a SINGLETON know that they are using it, because they must invoke the Instance method.

SINGLETON in Action

Assume that we have a Web-based system that allows users to log in to secure areas of a Web server. Such a system will have a database containing user names, passwords, and other user attributes. Assume further that the database is accessed through a third-party API. We could access the database directly in every module that needed to read and write a user. However, this would scatter usage of the third-party API throughout the code and would leave us no place to enforce access or structure conventions.

A better solution is to use the FACADE pattern and create a UserDatabase class that provides methods for reading and writing User objects.[3] These methods access the third-party API of the database, translating between User objects and the tables and rows of the database. Within the UserDatabase, we can enforce conventions of structure and access. For example, we can guarantee that no User record gets written unless it has a nonblank username. Or, we can serialize access to a User record, making sure that two modules cannot simultaneously read and write it.

[3] This special form of the FACADE pattern is known as a GATEWAY. For a detailed discussion of GATEWAYs, see [Fowler03].

The code in Listings 24-3 and 24-4 show a SINGLETON solution. The SINGLETON class is named UserDatabaseSource and implements the UserDatabase interface. Note that the static Instance() method does not have the traditional if statement to protect against multiple creations. Instead, it takes advantage of the .NET initialization facility.

Listing 24-3. UserDatabase interface

public interface UserDatabase {   User ReadUser(string userName);   void WriteUser(User user); }

Listing 24-4. UserDatabase Singleton

public class UserDatabaseSource : UserDatabase {   private static UserDatabase theInstance =     new UserDatabaseSource();   public static UserDatabase Instance   {     get     {       return theInstance;     }   }   private UserDatabaseSource()   {   }   public User ReadUser(string userName)   {     // Some Implementation   }   public void WriteUser(User user)   {     // Some Implementation   } }

This is an extremely common use of the SINGLETON pattern. It ensures that all database access will be through a single instance of UserDatabaseSource. This makes it easy to put checks, counters, and locks in UserDatabaseSource to enforce the access and structure conventions mentioned earlier.




Agile Principles, Patterns, and Practices in C#
Agile Principles, Patterns, and Practices in C#
ISBN: 0131857258
EAN: 2147483647
Year: 2006
Pages: 272

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net