Thursday, November 15, 2012

Singleton or Simpleton?

Everyone knows that the Singleton pattern is bad, right? But is it really?

What is a Singleton?

The Design Patterns book says: Ensure a class only has one instance, and provide a global point of access to it. The motivation for this pattern is to have one instance of a class. The book continues that the class itself should make sure that there is only one instance.

Implementation

A lot of space in the book is dedicated for the implementation. It appears that it is quite tricky to create only one instance of a class. The book's example, and probably most implementations in various languages require use of static variables and methods. A typical implementation would look something like this:

public sealed class Singleton
{
   private static volatile Singleton _instance;
   private static object _syncLock = new Object();

   public static Singleton Instance
   {
      get 
      {
         if (_instance == null) 
         {
            lock (_syncLock) 
            {
               if (_instance == null) 
                  _instance = new Singleton();
            }
         }

         return _instance;
      }
   }

   private Singleton() {
      //Prevent new Singleton().
   }
}


  • Lazy initialization with double locking. Create one only when it is needed.
  • Volatile instance variable to ensure atomic creation and assignment.
  • Thread safe. Uses a separate lock object.
  • Prevents new operator.

Getting it right is not that easy and usually requires quite a bit of knowledge.

Is it Evil?

Let us list some of the most commonly mentioned "evil" things:
  • The Singleton code is hard to get right and it instantly becomes boilerplate that you copy to other Singletons over and over.
  • Singletons are shared instances, and so they must be thread safe. This easily forgotten fact causes many unexpected problems.
  • Can't really subclass a Singleton - you will break Liskov and other OOD principles to do this.
  • Related to the previous; you can not make static instantiation functions abstract or virtual, thus negating any kind of abstract factory type behavior. You are stuck to tying the creation of the instance with the Singleton class itself. You can try returning an interface but it still requires a base class to know about its derivates directly or indirectly.
  • Static method access allows you to hide API dependencies. Your API does not have to declare which singletons it is using inside. The behaviors that classes using singletons demonstrate can be unexpected and surprising - from the outset it is not obvious that code executes database queries, or does some other "magic" under the hood. Note that this does NOT remove dependencies, but it makes them implicit!
  • Switching singleton implementations is hard and so, also, unit testing becomes hard. It might not be easy to do unit testing when you can not mock the behavior of the singleton, or you many not replace the dependent behavior at all because the code under test calls a singleton.
  • Using Singletons (like any global) lavishly means that you are hard wiring your software leading to monolithic designs. When widely used, software starts to resemble procedural designs familiar from C and comparable languages.
  • Singletons are Singletons, until they are not. We sometimes make a wrong design choice, or a bad prediction; what used to be ONE, no longer is not. Sadly, you are now stuck with it, and your only reasonable option in short term might be to break the Singleton and create many copies, usually by delegation or embedded factory inside the instance() function.
  • Ever-lasting reference to the single instance can not be garbage collected without active "memory management". If your Singletons are big, and hold references to other things, you might be holding onto more stuff that you originally planned. 

But It is Popular!

All of that does not make the pattern very appealing, but still, there are always Singletons floating around. For one, some designs call for one instance of a class. It is quite legitimate to have such a requirement but somehow I doubt that the single instance argument made it popular.

Once developers learned how to create Singletons, as the Design Patterns book showed how, it became a very popular pattern to use. The appeal of easy access to one instance of a class made sure of it "success". Many design questions were now easy to answer - you just call for the Singleton when ever you need functionality of the object, right there in the code where you needed  it. You can do away with properties and constructor arguments, and you no longer have to concern yourself with having to hand in the dependencies the old fashioned way. From outside, classes look quite neat without those pesky argument lists and property pollution. Looks are deceiving.

It just became another way to go back to programming with "global variables". I also call these kinds of static accessor methods as a way to do "Russian doll design". You'd have to keep opening the Russian dolls to get to the bottom of the rather surprising functionality.

Implementation Dictates Context

What really hurts this pattern is its implementation. It is fine to have a requirement of a single instance, but it may not be fine to implement it with static variables and methods. When we talk about patterns, we also must remember what they are; design that solves a common problem within a context. Yes, within a context.

The Singleton pattern as presented above only exists in one particular context based on its implementation. That context is very limited to the utility of "static" variables and functions in the particular programming language. Yet, people treat it as a general purpose solution, and that ultimately dooms this pattern. Its utility might be very limited, contrary to its popularity.

Consider what happens across processes, or even class loaders that exist for many languages that have Virtual Machines. You can not guarantee a single instance easily with this kind of implementation and you may be surprised to find your application misbehaving if you expected to be safe from such things.

Utility Based on Context

Rather than bashing this pattern endlessly, let us expand the horizons a little bit and concentrate on the idea of patterns in a context. The Singleton "idea" is actually one that we use all the time, widely, in bounded contexts. The idea of Singleton - one instance in a context - is very useful.

To fix our approach to Singletons, we must first separate the creation of a Singleton from its implementation. In short, this means that the application of static members or functions is not allowed, and that any class can be a Singleton, or not, without us having to change anything about that class. We will only change the creation of that class, usually by configuration.

This is a solved problem these days, and has been for quite a long time. Popular Dependency Injection (DI) frameworks support the Singleton in different contexts. For web applications we might use "Request", "Session", or "Application" context, and DI containers like Spring can manage the context for you. We can also have the more traditional Singleton within a single container.

Anyone who uses DI frameworks has used these kinds of Singletons, and used them widely. Yet, we do not really think about it a whole lot, because we do not have to. The "evil" parts have gone away. Using Singletons like this, in a context, works naturally and there is no ultimate requirement to have just ONE - just one in a context.

Legitimate Uses of the Traditional Singleton

Since we talked about the context, there must be a context where the traditional Singleton approach is still valid. Let's call this a single instance in a process or class loader.

Typical examples include getting access to cross cutting features such as instrumentation or infrastructure that has to have a single point of entry in that context. A typical DI context might not be enough to solve this problem, and even the DI contexts must be created and initiated somewhere.

The traditional implementation still has many down sides with instantiation of different implementations, so what many approaches to "access roots" prefer is some kind of dynamic "service locator" that allows a configurable implementation:

var instance = Locator.GetInstance<IMySingleton>();

The service locator can choose the appropriate implementation, and can also choose how many instances are created. The access still clearly uses statics, which brings us many of the same woes that Singletons did, such as Russian doll design.

This may not be so bad with the named cross cutting infrastructure, such as logging, or configuration. Purists would still prefer to use techniques like AOP (Aspect Oriented Programming) to provide "transparent" logging and instrumentation, but this might not give enough insight to what you should log, for example. For practical purposes, you see log instantiation code similar to the example above.

Many, if not all, DI containers also double as service locators. However, this is not their primary mode of operation and they should not be generally used to fulfill this purpose.

But what about the actual traditional Singleton? It may just be that there are no real dependable uses for it, or  it should be very rare. We are still forced to deal with such code because we deal with libraries and other dependencies that use the Singleton pattern. Their use should be limited to as few spots in your code as possible.

Static Injection

I am going to mention one special case that came up recently, which involves DI containers and Singletons.

Having to inject dependencies during object creation can have its own problems. Sometimes people talk about Spring configuration hell, where injecting dependencies becomes nightmarish in the sea of hundreds and even thousands of classes. 

To combat some of these issues, it might be acceptable to use Singletons and static injection features of DI containers. Here you would let the DI container to call the instance factory function of the Singleton to create the instance, and then inject its dependencies. It is then possible to use such Singleton classes directly instead of injecting the instances. This approach can have the same alluring appeal to be replicated everywhere as the Singleton itself, but it also has most if not all of the problems.

Yet, this approach can have limited use:

  • Make sure that the Singletons are used only in one closed and limited context. These Singletons may not be shared or reused between instances of several classes, for example.
  • Do not use this approach for your business code, infrastructure, or code that is widely used as lower layers of your software where you can likely never guarantee that Singleton is the right way to go. Using this approach is OK only on the top application layer.
Post a Comment