1. 14
  1.  

  2. 9

    I’ve written Java code professionally for about 15 years. My relationship with singletons have gone through these phases:

    1. Singletons are cool! Let’s use them everywhere. (About a year)
    2. Never use singletons! Singletons are code smell. It breaks single-responsibility principle, causes contention problems, means very tight coupling, means code is often hard to test. (About 5-7 years)

    Until finally, where I am today:

    1. Ok. Let’s use singletons where it makes sense.

    We have a very large enterprise Java application and use singletons in a couple of places. Client-side caches, server-side caches, connectivity classes from client to server, global statistics framework, logging framework, global system wide properties and defaults. That’s about it.

    As with everything in programming, I say as in The Hillstreet Blues; “Let’s be careful out there”

    1. 4

      From the examples you listed, why not just inject those as dependencies into the calling scope? Why do they have to be singletons?

      1. 4

        Because they are literally used everywhere. We don’t want to be injecting them everywhere.

        Also, singletons are easy to use safely, if well designed. Sadly, the majority of our developers are very inexperienced, and to be frank, not all that great developers. It’s easier for them to get connections right, logging, or proper use of caches, when it is available, looks, and feels the same everywhere.

        Also, some core code that is highly sensitive to concurrency problems is hidden and handled behind the singleton. Such that the developers don’t have to think about things like that.

        1. 1

          Don’t they make testing harder?

          1. 2

            Yes. Definitely. Testing wise, it’s a mess. We have had to create very specialised mock versions of them that are as easy to use such that we can switch them out for mocks altogether for unit tests.

            We have very few unit tests compared to lines of production code. Instead we try to do more continuous testing using BDD with Gherkin that runs against live environments where we can do more system and end-to-end testing.

            But yes. For unit testing. These large singletons are a headache.

    2. 3

      In my only experience architecting a large C# application, I never reached a satisfactory conclusion on this topic.

      In practice, singleton never seemed to solve any of the issues that led to my exploring it, instead acting as a temporary tonic to my OOP design anxieties while obscuring implicit behavior and making my application harder to reason about.

      My final design in said application had me explicitly instantiating at the main entry point a small number of objects representing legitimately global systems (datastore, audio controller, etc.) and explicitly passing those objects as parameters until functions became too yucky, then refactoring to wrap them in something akin to a master application configuration object with a rigid interface.

      The dissatisfaction I had and have with this final implementation seems to be aesthetic, like it violates some deep OOP principles (likely), but which aren’t solved with singleton.

      Using singleton made me feel clever as a “consumer” of my own singleton each time I wrote a function call, but made the program harder to reason about because the implicit behavior was still implicit, just implicitly implicit instead of explicitly implicit.

      1. 4

        disclaimer: these are observations from the code bases I’ve worked on, i don’t know your specific situation

        explicitly passing those objects as parameters until functions became too yucky

        Every time I’ve reached this situation the fault was bad abstractions. The methods were doing too much or needed to be aware of too many things. Later on during development, these also emerged to be the spots where it was difficult to hold the complete logic in my head.

        even when said state is legitimately global

        There is always global state the point is that the lower level code shouldn’t be aware of it or depend on it. The lower level code should only know what it was provided.

        I’ve try very hard to not write any singleton classes but working with a large team with tons of code, i semiregularly stumble upon something that resembles a singleton, it might look like snippit 1 or 2 from the article and I’ll refactor it to snippit 5.

        I am not sure if this is useful, it took me a while to realize that patterns are not tools to solve a problem they are categories of solutions. If you end up using a solution that looks like a pattern refactor the code to fix the problems that the pattern frequently has/prevents.

        1. 3

          every time I’ve reached this situation the fault was bad abstraction

          Undoubtedly true in my situation. The most hair-pulling breakdowns in coherent design always appeared in sub-systems I had the least experience with.

          Audio is a useful example. If the audio controller was told to play a sound, how and when would it actually play? What if something else was already playing? Did the controller need to understand the caller and why the sound was triggered, and thus depend on every potential caller? Or if the caller passed the details, didn’t that just flip the dependency and unravel the encapsulation of audio logic?

          Presumably someone with deeper experience would have started from some sort of informed design, but I bolted on sound after everything else and learned that you had to think about such things only when I stumbled over them for the first time.

          1. 2

            The most hair-pulling breakdowns in coherent design always appeared in sub-systems I had the least experience with.

            Absolutely, this happens all the time.

            learned that you had to think about such things only when I stumbled over them for the first time.

            i am still pretty bad at this, so i first think, then sketch out what i would do and then do it 2-3 times. it’s a little slow in beginning but keep development faster in the long term.

        2. 1

          My final design in said application had me explicitly instantiating at the main entry point a small number of objects representing legitimately global systems (datastore, audio controller, etc.) and explicitly passing those objects as parameters…

          I do something like this in C# using the Ninject dependency injection framework. It offers various scopes for those to be injected in (e.g., singleton, thread, ASP.NET request, etc.). This way, you don’t really have to write any singleton classes as such, and your calling code doesn’t have to do anything special other than accept dependency injection. It makes testing easier than with an actual singleton, too.

        3. 2

          This would be a good set of questions to work through in and interview.

          I really like the fact that Scala comes with Singletons built into the language using the object keyword. It makes this entire process a lot simpler.