Monday, November 27, 2023

Are singletons evil?

Ask your average developer what design patterns they use in their code and a high percentage will say "Oh yeah of course, I use the singleton all the time." they might even name drop the gang of four. In an earlier post I linked this compelling argument by Steve Yegge basically stating the pattern is overused and does more to defy good OOD than it does to help it.

Steve's post is one of many singleton's are evil sentiments posted all over the internet. I don't care to go about writing another Dijkstra-like "considered harmful" diatribe. Rather I'd like to give my take on some of the more interesting and popular talking points that either defend or abhor the use of this pattern.

Common talking points and my take on each:
  1. A singleton is basically a memory leak
    1. In terms of languages that implement a garbage collector this thought may be a bit pedantic, but if a singleton allocates memory that memory is not released until the application ends. Unless you explicitly implement a destructor, which for a singleton opens other problems. In C# you wouldn't be able to instantiate the same singleton within the same application domain again after destruction. Also since singletons tend to get referenced all over the applications that use them; what if some object still depends on your "basically global variable"? If I were still a C++ developer I might be more concerned with the memory leak issue. Also most alternatives to a singleton class will likely end up in the same boat (as far as memory management) as the intention is usually a long living class anyway. But... your not married to it, and you have control if you find another way besides singleton.
  2. The singleton pattern breaks the "S" in Robert Martin's "Solid" - principles of object oriented programming mnemonic. This in my opinion is true. You are creating a class that is concerned with performing some function, and it is concerned with making sure it only gets instantiated once. A factory would be a better place to regulate how many times a class can be instantiated. This leaves your code flexible for the future, less tightly coupled, and better suited for unit testing.
  3. Singletons are a lazy, procedural approach to adding globals
    1. It has been suggested more than once that the gang of four patterns (while having many greatly useful patterns) were introduced to cover holes in the language du jour of the day C++. Was singleton one of said hole pluggers or was it an attempt to make procedural developers at home if they were not be able to wrap their heads around good OOD? I don't know. It's all speculation, and I wouldn't know where to hunt down the true story. However the idea of patterns defined 20 years ago in a programming landscape that looked like C++ was the present and the future, might have been defined around the strengths and weaknesses of said popular language doesn't feel too far fetched. Is it true? Who knows, who cares. All I care about is; does this pattern work well in modern OOP.
  4. Global state effects the ability to write deterministic code
    1. It's a fact that global state makes code less deterministic. Lets use testing as an example One test might change the state of this global, affecting the outcome of the next test to run. Therefore a unit test could have different results and different side effects just based on the order unit tests are called in. (Kinda complicates isolated unit testing doesn't it?)
    2. One of the articles I linked above points out that it is not just the singleton class in question that is global. Most singletons are complex objects holding references to other complex objects. Thanks to their transitive relations all those reference instances are now part of the applications global state.
  5. Is there a situation where a singleton is the best solution?
    1. Example attempt to answer to this question on dofactory.com's forum: "A few days ago, I had to implement holding a list of loaded assemblies and its references during program-execution. As this is a bigger project, we're talking about 150 dll-files to look at. In that case, I just didn't want to load the same data again and again whenever I need to get one of these assemblies. Therefore, I used a singleton-class, which creates this certain list on its first call and holds that values until the application gets closed."
      1. This list could be a lazy loaded static member of an instance class. But, isn't that still global state? Yes. 
    1. Common uses
      1. A logger class. Many brave warriors have tried to use a logger class as an example of a good use case for a singleton class only to have many others easily refute it as a bad example. So rather than come up with a new counter example I'm just gonna quote someone else. "Today, I might think that I need only one logger instance. But what if I realize­ tomorrow that I need two? That’s not so far fetched. We may have one log we write ad-hoc messages intended for debugging purposes, solely to be read by devel­op­ers, and another for­mal­ized log, where structured mes­sages are writ­ten when pre­de­ter­mined events occur, so that the appli­ca­tion can be mon­i­tored in pro­duc­tion. Sure, we could define the two as com­pletely sep­a­rate classes, and then we’d only need one instance of each (but then we’d start dupli­cat­ing code). Or we could use the same log instance to write to both logs (but then the log­ging code would become more com­plex, hav­ing to inter­leave two sep­a­rate and non-overlapping logs. Once we’ve accepted that an appli­ca­tion may need more than one log­ger, shouldn’t we do our­selves the favor of ensur­ing that our log­gers can be instan­ti­ated more than once, just in case it turns out to be the right thing to do? We’re not even adding any com­plex­ity, there’s no cost asso­ci­ated with this. On the con­trary, we’re remov­ing sig­nif­i­cant com­plex­ity. Thread-safe sin­gle­tons are sur­pris­ingly hard to get right. Depen­den­cies between sin­gle­tons are tricky and cir­cu­lar ones can cause them to blow up in all sorts of fun ways. And let’s not even get into how to han­dle any­thing our sin­gle­tons might do while the appli­ca­tion is shut­ting down. What if the data­base sin­gle­ton tries to write a sim­ple “good­bye” log mes­sage to the log sin­gle­ton? What if the log sin­gle­ton got destroyed before the data­base one? Ouch."
      2. A data access class. My personal experiences with singleton classes. In nearly 12 years of professional development I have released 3 singleton classes into production. All three times it was to serve the same purpose and all three times I concede it was probably the wrong choice. In these three occasions I was writing a class that instantiated and gained access to some form of API that had specific credential and configuration settings. All three times my singleton wrapper would be used throughout the application to garner some form of data access using these API's (whether it be an ORM or a Rest API). In all cases there were to be different domains for these API's, and inevitably different permutations or completely different API's all together. So why make my local wrapper global. True I didn't want to rewrite the code to instantiate and authenticate. But why make it global? So what if different parts of your application authenticate. Is there a RESTful API that cannot handle that? Is there an ORM that cannot handle that?
      3. A config class. This is one of the easiest to refute. You have config data and it gets shared amongst various layers of your application. You create a singleton class that grabs config info out of storage and holds the data for your various layers to utilize right? Wrong, what if you want to change the config values in a layer of your application that takes user input for changing config values. You have the changes and have yet to persist them to storage. Now all your layers have the changed values. But persisting to storage failed or the user changed their mind. Meanwhile some other layer has acted on those changed values. Example: The user went into the config UI and changed logging from "verbose" to "standard". Some other layer who has a reference to the config singleton has an exception. It goes to log its exception and shows standard logging details instead of verbose. The user changes their mind and cancels the change. But it's too late they've missed out out on verbose logging for that exception. Some might say there is an easy fix for that have the config management UI write to a different object representing the config object, and have a method inside the singelton config object that takes this other config object. First of all that certainly isn't a DRY approach and for all intents and purposes the behavior you are achieving here can achieved in a way that allows more flexibility. Create a config object that can be instantiated. It holds the members that define your config attributes. Expose your CRUD operations. What about reloading the config info over and over by each layer. This is where dependency injection comes into play. Via constructor injection or setter injection your class can require the config object. Here is an example config object implemented in Ruby. (Not my strongest language)At first it would appear there is a problem. A doesn't know B persisted new config data. However its not a problem, its just the lack of a side effect. If the config were a singleton A would immediately have the new data, but what if that creates an undesirable side effect for A? Should B changing its object state cause side effects in every layer of your application who is concerned with config values? You may have coded anticipating this side effect, but what if there was a situation that didn't want it? Or a situation you didn't think about? Suppose A is currently in the middle of an operation based off the config values it originally loaded. As a developer you may want the choice to load the new config values or you may want to hold off until you have finished your current operation. Would not the Observer pattern help you here? Your layers will get notified, and then you get to make your own choice.
        1. *Note of course there is still global state going on here. It's access is controlled but it is there no the less.
  6. What is the real difference between a singleton class and a class with all static members?
    1. In a nutshell; one gives you back an object, the other only gives you access to methods. A static classes constructor is private cannot be called, and takes no parameters. Both can contain state. A singleton can implement an interface and enjoy polymorphism.
    2. Both patterns are inherently non-thread safe. You have to implement safeguards to ensure thread safety. If you are developing in C# there are some really tried-and-true instantiation related thread safe singleton implementation examples. If you are in Ruby using Ruby's built in singleton functionality instantiation/constructor thread safety shouldn't be a concern either. But that does not guarantee the methods in these two types of classes are thread safe. You would have to wire that up as well.
      1. One could argue what pattern or constructs are inherently thread safe without explicit safeguards. (immutable types). The argument with this pattern is there is an easily viewable and understandable issue of thread safety that arises with every implementation surrounding constructing a singular global object or its methods.
      2. Using singletons incurs overhead. (see CPU cycles) You are calling the instance accessor every time you access members of a singleton class.
  7. If singletons are good enough for Yukihiro Matsumoto and the Ruby language why aren't they good enough for me?
    1. Singleton pattern versus singleton class. Not to be confused, these were touched on earlier in my post.
    2. Ruby's built in features accomplish a lot of interesting behaviors like assigning a method to a single instance of an object. Ruby unlike other languages does not have a class structure that can contain instance methods and static methods. This may be in part due to the fact that the classes we define using Ruby are actual instances of the "class" object, and static methods are instance methods on an unnamed singleton class instance who exists in the inheritance hierarchy just before your actual class. All this is to say that the "everything is an object" nature and meta-programming abilities have led the Ruby team to use singleton objects within their class implementation. But are those singleton or eigenclasses, or metaclass (Some argue that the actual "Class" class is the truer metaclass save that for a different post) objects following the Singleton design pattern? Do they introduce global state all over the place constantly? How about a test? Why? This may go back to the time period they built in these features. Maybe less was known and observed about this pattern back then. Could they accomplish the same features without the use of the singleton pattern? I would like to think so, but I have no definitive proof. However, our choice to implement business domain applications using the Singleton design pattern should not default to "why not" just because the Ruby language incorporates them. Its difficult to tell a long time Ruby'ist to rethink their usage of a pattern that gets used abundantly in the implementation of their chosen language, but no code design is perfect, even the code written by industry legends.
  8. Alternatives to the singleton
    1. Mono­state pattern
    2. Dependency injection

conclusion: A class that exists singularly as opposed to a class defined using the singleton pattern. Have a single class if it suits your needs, just don't ensure it is single by the use of a pattern that introduces unnecessary dependancies and global state. We're never going to get away from global state completely. The dire to have global state doesn't come magically out of thin air.