Singleton Global Problems

Moved here from SingletonPattern

Global singletons cause "utility functions" to end up in odd places, because it doesn't matter where you put them, you can still call them! So people will add them to whatever file happens to be open in their editor at that moment! The separation of functionality into different classes soon becomes meaningless, which means that (worse case) if you build a UnitTest for one class, you will have tested 10% of ten different things, instead of 100% of one thing. -- DanielEarwicker

Don't build unit tests for classes. Don't feel constrained because a dozen years ago some tester declared that a class is a unit. Build your unit tests for units of functionality.


Singletons are like sharks. They cruise along unnoticed under the surface, then pop up and bite you somewhere you weren't expecting. It's impossible to tell where a singleton is used without doing something like grepping through your code (or a find in files in a decent editor). They are essentially hidden parameters. To give an example:

   public void printReport(String name, List stuff, PrintService printer) {...}
is (to me) far better than
   public void printReport(String name, List stuff) {
      ...
      PrintService.getInstance().print(name, stuff);
   }
The obtaining of the reference to PrintService is buried in the method, with no indication in the method signature that it was needed. It also makes things much harder to test. If I have a parameter, I can substitute any other implementation of the same interface (ie. MockPrintService) easily. If it's a singleton, I cannot prevent the code trying to use the production implementation.

-- DarrenHobbs

   class ObjectUsingPrinter
   {
   public:
      ObjectUsingPrinter(Printer printerToUse= PrinterService::Singleton());
   }
This way it can be clearly defaulted and UnitTested without every subsystem in the entire system knowing how to bind to a printer. In my world, though, a singleton is a service access point and most cases will not create the object. It is instead set by some context that knows how to resolve the default printer against all the possible selection mechanisms.

-- AnonymousDonor


Imagine if a class A was using class D, which is a database access library. So within the implementation of A, it keeps declaring instances of D and using them to query a database. To make a UnitTest for A, you have to declare a stub version of D that returns typical dummy values.

The singletons you describe above are no different from class D, really. I don't think you can get away from that. The dependencies of a class are not always made obvious in its interface. Personally, I think other the problems you mention are more annoying, and I would be itching to sort them out - otherwise there's a danger that your tests will be as confused as the classes. -- DanielEarwicker


After re-reading all the above discussion, maybe something we can all agree on is that globally accessible (or accessed) singletons are evil, for largely the same reasons that any other global is evil. True? False? Inadequate?

We often forget why GlobalVariablesAreBad. GlobalVariablesAreBad primarily because such data can change in unexpected ways. When we encapsulate it in an object, we gain some control of how it can change. Encapsulation (whether OO or not) makes global data less bad.

But even when we have objects to mitigate the first problem, global objects can produce implicit coupling that we should monitor carefully.

And don't miss Kent's point that easy access to global communication relieves any pressure to find the proper limited visibility for objects. Globals can help you to be lazy and sloppy.

Finally, GlobalVariablesAreBad is not a cut-and-dried issue. Limited and careful use of global things can (arguably) be legitimate.

A database, for instance. What's significant here is that the data has to outlive the application which contains the classes and objects.


I disagree that globals are ever a good idea.

Globals are never really 'global', therefore should not be declared as such. Wherever a global appears, there is an implicit scope that I believe should be exposed/encapsulated, and passed as a parameter to users of that scope.

Singletons provide some encapsulation, but unfortunately Class has 'global' (actually within my local classloader [see ClassLoader?]) access and so is not forced to be passed as a parameter.

A context object is not global and, therefore, has no implicit scope which can be accessed from the middle of the object without a corresponding interface. Therefore it must be passed into the object (in some sense).

My approach is to link any context into an object when it is instantiated. This is less dynamic than a parameter, but avoids the overhead and excess coupling. YMMV. -- RichardHenderson


KentBeck mentioned on the OTUG mailing list that he thought that SingletonsAreEvil. I think the point he was making was that the global accessibility to a singleton can lead to diverse parts of the system becoming dependent on the singleton, including parts of the system that were never intended to use the singleton. They end up being the global variables of OO. -- JamesCrawford

A SingletonPattern is a strategic decision that needs to be made at the highest level of the program. Unless the Singleton pattern is completely hidden from the outside world, it becomes a GlobalVariable with all the coupling problems that people have discovered in days gone by.

Construction and destruction are problems at program startup and shutdown. I hate them. I try and implement objects that will end up as Singletons as regular, everyday objects. If and when these object gets reused, it allows the person that is reusing it to not use it as a singleton with no trouble. In general, making something a singleton is a lot easier than unmaking something a singleton. To me this is always a sign of caution in a pattern.

-- FrankMcGeough

I'm having trouble with this. Is it that SingletonsAreEvil or that GlobalVariablesAreBad and SingletonsAreOftenGlobalsInDisguise?? I'm not sure I understand the former, though the I understand the latter. -- BillJamison


Moved from SingletonPattern

KentBeck posted this to the OTUG mailing list in March of 1999:

Once you have created two, there's nothing to stop you from creating ten or fifty. And easy access to global communication relieves any pressure to find the proper limited visibility for objects.

For example, I was working on unit-linked life insurance. We needed daily net asset values and exchange rates (a CurrencyExchange). But obviously there is only one set of values and rates. So we used Singleton.

We got one test running with one set of rates and prices. Then we were given another test, but it had been written with another set of rates and prices. We briefly considered swapping the Singleton out before we started each test and installing our prices, then putting things back when we were finished, but the stench prevented us.

The alternative was to find those objects in the system that really needed prices and make sure they all had access to a CurrencyExchange with less-than-global visibility. Grumble grumble. This is going to screw up our design as we pass the CurrencyExchange around a million places. This is going to take too long...

It didn't take very long. The new design communicated perfectly clearly where prices were needed. And we discovered a couple more simplifying refactoring along the way. And new tests were easy to write (we only had to specify the prices and rates we were going to read), easy to read (you could see the data right there in the test instead of having it hidden in a global somewhere), and fast.

Without the pressure of testing, though, we would never have found those improvements. And all because of that dang Singleton...

-- Kent

The clue here is that the rates were different. This is not a case where the rates actually came from a single, external source (for instance, supplied by an outside bank). Here you had rates and prices that could vary, so the Singleton was an obviously wrong choice. Had there been but one path to conversion rates and prices the Singleton would have made all the sense there is.


It is true only if the singleton is used as a GlobalVariable, i.e., it is shared by several clients that can take advantage of its uniqueness to interact (indirectly) with each other. This is one common conception of singletons. Another kind is a class in which being a singleton is part of the implementation, not of the interface.

The DesignPatternsBook describes singletons as one of the CreationalPatterns: a technique to share between clients an instance of a class because you don't want to create more than one. On the other hand, a GlobalVariable is shared because you want every client to refer to the same data. I think that in the same classification global variables are one of the BehavioralPatterns. Often, clients don't care about the identity of the singleton instance; uniqueness and preserved identity might be needed for the correct or efficient operation of the singleton, but not visible outside.

For example, suppose you have a Logger interface to represent objects that can log complex, structured messages. A Logger implementation that writes naively to the standard output would interleave the messages from different, concurrently invoked instances; a Logger that inserts rows into a table in a SQL database can work correctly with any number of instances, but it would be better to conserve resources like database connections; a Logger that writes to its own file is expected to have as many explicitly created instances as needed.

I think there can be some confusion between client-visible global variables and singletons encapsulated behind FactoryMethods or factory classes; some of the discussion on this page deals with the problems of the former and some with implementations of the latter.

-- LorenzoGatti

Your Logger example lacks the instance of an implementation that invokes an IncidentReport, passing all the pertinent details to a unit responsible for the logging of the message. The caller doesn't know or care how the log entry is made, just that the event is logged. There would be a corresponding ReportLookup or something that would find the logged events and display them.


I think Lorenzo's Logger example is a good one; if a separate InstanceLogger is required to manipulate state data into a loggable string, it should be implemented as a separate class (the singleton should always be static). I am currently reimplementing my custom error handler (which previously was a method defined in a base class) to use PHP's set_error_handler() and trigger_error() functions, which I hope will make my library code more portable and easier to test. Because there can be several errors, triggered by various objects, and because Error::Display() is only going to be called once, it is important that all code shares the same error handler state. (in my example the built-in interface side-steps the global/pass-by-reference issue, but I think it illuminates his point about ensuring a single interface to a single resource)

-- kenneth (who is new to patterns and TDD)


I wonder if perhaps the bulk of Singleton problems could be address by a Singleton-within-Context pattern, where the Singleton is a unique instance only within a particular Context (whatever that means to you, but let's not assume it's Global). Therefore, I might have a Testing context in addition to a Running context.

Stubby pseudocode:

 class SingletonWithinContext?
 {
   class Holder {
     Holder(context) {
        instance = instantiateForContext(context);
     }

SingletonWithinContext? instantiateForContext(Context c) { if (c instanceof TestContext?) { // do this... } else { // to the other... } }

...... }

One could go a step farther and say that any class requesting a singleton instance would pass itself as a context. The instance instantiated would be dependent on the class type (or inherited type) of the caller. The Context type would be nothing more than a marker class, with not methods or fields. If, later, you determine that a different singleton instance is needed for a caller, change the callers Context base type. Which singleton instance is instantiated can be based on a Mapping of Context type to Singleton instance type.

-- JeffLowery


See: YouCantEncapsulateEverything

CategoryPattern


EditText of this page (last edited May 25, 2008) or FindPage with title or text search