1

I'm working on a small project and I've decided to move over from DI to singleton pattern. Although I know 2 ways to do singleton.

The first one is where every non-model class is a singleton. This means:

UserController
DatabaseHelper
ConfigurationModule
FriendComponent

Are all singletons, however:

User
UserFriendship
DatabaseConnection
DatabaseConfig

Aren't since they're all models.

However, the second way I know is:

I have one main singleton class (I'll use class Program as example). The class looking something like:

class Program
{
    private static Program _instance;
    private readonly DatabaseHelper _databaseHelper;

    public Program()
    {
        _databaseHelper = new DatabaseHelper();
    }

    public static Program GetInstance()
    {
        if (_instance == null) 
        {
            _instance = new Program();
        }

        return _instance;
    }
}

I know this is mainly subjective but I was wondering which of these 2 (or if both not) is best to use.

Navine
  • 31
  • 6
    "I know this is mainly subjective but I was wondering which of these 2 (or if both not) is best to use." Neither. There is no excuse, ever, to inflict the singleton anti-pattern on a piece of code. – David Arno Jul 10 '18 at 07:41
  • Then what'd be the best way to do it without that, DI or god class which also appears to be bad from what I've heard? – Navine Jul 10 '18 at 07:42
  • @Navine try new – Ant P Jul 10 '18 at 07:43
  • @AntP What do you mean? – Navine Jul 10 '18 at 07:44
  • 1
    Why do you need to enforce there is only one? Either you have mutable global state, or you don't need to enforce only one – Caleth Jul 10 '18 at 08:25

2 Answers2

11

I know this is mainly subjective but I was wondering which of these 2 (or if both not) is best to use.

Neither. There is no excuse, ever, to inflict the singleton anti-pattern on a piece of code.

If you need Program to be a singleton, then create it once and pass it around you application via constructor and method parameters. That is, after all, all that dependency injection is when stripped down to its pure form. If you are having trouble with DI then the chances are, you've dived head first into using a DI framework and ended up tying yourself in knots with its complexity. So start at the beginning: pass values around using parameters.

The singleton pattern is just a glorified global variable, with all the action at a distance problems that globals cause. It makes the code hard to maintain and very hard to test. So just don't use them, please.

David Arno
  • 39,270
  • 5
    While I agree with the main part of this answer (regarding DI frameworks vs. parameters), I think absolute statements like “there is no excuse, ever” are deeply unhelpful and intellectually lazy. On this site we see so many questions that stem from blindly following best practices. I don't think we should be encouraging that kind of thinking. Software designs are not morally right or wrong, there are just approaches that are more likely to work. I'd rather say: “In my experience, singletons are unnecessary or even problematic. We don't need them here because we can simply do X, Y, or Z.” – amon Jul 10 '18 at 09:33
  • 3
    @amon, I agree that too often some folk (and I'm one of them), use absolutes around coding rules, when "most of the time, you should not be using xxx" is better advice. But there are some cases where I feel that absolute position is justified. The singleton pattern is one such case in my view. So a simple challenge for you: provide me with a valid use case for where a static, globally accessible, singleton is the only viable solution and I'll accept I'm wrong to adopt that absolute position. Go... ;) – David Arno Jul 10 '18 at 10:05
  • 3
    I like the simple statement of absolute truth. We don't need to preface everything with "in my opinion" or suffix with "except for edge case x" people want succinct answers to their problems, not essays – Ewan Jul 10 '18 at 10:10
  • @DavidArno: Challenge accepted. A settings class which retrieves its data from an external dependency. If the settings never get called, processing the dependency is unnecessary (hence why a singleton is better than a static created object). If the settings do get called, you only want to process the dependency once (hence why a singleton is better than a normal object). The flow logic (load only if needed, and load only once) is exactly that of a singleton. This also applies to other cases of cached data where fetching the data is a non-negligible performance cost. – Flater Jul 10 '18 at 10:38
  • 1
    @DavidArno As an aside, "provide me with a valid use case for [foo] where it is the only viable solution" is an overstatement. Viability depends on outweiging pros and cons, and is therefore a contextual evaluation. You can't generalize a claim that "[foo] is(n't) the only viable option" unless you have a particular situation in mind. The only thing you can evaluate objectively is that it compiles and doesn't throw runtime exceptions. – Flater Jul 10 '18 at 10:40
  • 4
    @Flater: you can still inject an object that loads something lazily and caches it without using the Singleton pattern. – Bossie Jul 10 '18 at 11:11
  • @JanVandenbosch: Lazy loading is essentially why singletons were created as opposed to statically creating the object before knowing that its existence is needed. There are different ways to lazy load something, yes. But the assertion that you can only prove the usage of a singleton (or any other pattern, for that matter) if there's no other possible way to achieve something is ludicrous. That's like arguing that you can never justify using a boat when you're able to swim (and completely ignoring travel distance). – Flater Jul 10 '18 at 11:29
  • @Flater, "Challenge accepted. A settings class which retrieves its data from an external dependency". No. Why on earth would you choose to make your app almost impossible to test different configurations without hacking that singleton when a simple lazy-loading object can be created and injected? That "user case" for singletons is comprehensively rejected. Want to try again? – David Arno Jul 10 '18 at 11:41
  • @DavidArno: A config file (which is an example of an external dependency) is made specifically because it is easily swappable. It exists specifically because it makes it easy to test different configurations. – Flater Jul 10 '18 at 11:48
  • @Flater what happens when the bottleneck of your tests is they are thrashing the disk cache loading variations of settings? – Caleth Jul 10 '18 at 11:58
  • @Caleth Then you shouldn't be using config files but that's not the point of the discussion on using singletons. Your argument defeats the premise of the use case I offered. – Flater Jul 10 '18 at 13:03
  • 3
    "A settings class which retrieves its data from an external dependency" -> except you don't want an external dependency when testing clients of settings – Caleth Jul 10 '18 at 13:25
  • @DavidArno This is not a case where I disagree with you on a technical level. We both know that singletons are never strictly necessary, but that is a Turing Tarpit kind of argument – singletons might still be useful. … – amon Jul 10 '18 at 16:21
  • Cases that come to mind where singletons can be useful: (1) Things that are already global, e.g. an object representing the env variables of a process, or resources in embedded systems. (2) Things that should only exist once per process, e.g. an event loop in an application. (Although this doesn't play nicely with some testing strategies.) (3) When parameter passing is not possible without huge architecture changes, e.g. given a call graph “my code → 3rd party code → my code”. In one such case I used singletons as a DI mechanism to make my code testable and be able to refactor it safely. – amon Jul 10 '18 at 16:23
2

Consider this:

If a singleton is the solution, then what is the problem?

But I only want one instance of a class!

Fine, then create a single instance.

But why paint yourself into a corner with a singleton when there is no upside? Consider the canonical example of logging. You might only want one log now, but you may want a number of them in the future.

Far from making code simpler, singletons tend to have their own specific syntax in code (and in frameworks too). There are a persistent blight on code bases (legacy ones in particular) and I suspect history will rightly judge these "silver bullets" as a mistake.

Robbie Dee
  • 9,815
  • 2
  • 24
  • 53