Depending on what you are trying to implement - passing an extra variable everywhere is just creating noise (because potentially you have to have an extra parameter to EVERY function or method). I believe in KISS - everything should be as simple as possible but not simpler. Believe it or not there are valid uses for a Singleton which, many would agree, include a simple logging class [1].
One could argue that you could just create it in the section of code you want to log - but that just creates noise and you end up repeating code.
logger = Log() # run code to open the file to the last position
logger.log("test")
vs
Log.instance().log("test")
Now imagine this was a multi-threaded application - singleton arguments are much different.
> Singletons are really just as bad as global variables. Why? Because they are global variables.
Everyone has their own opinions - but you can't make sweeping generalizations. I'm not saying a global variable is appropriate in every situation - but every language, and project, is different. There are even different dialects of C++ [2].
> you have to have an extra parameter to EVERY function or method
Dependency injection provides a pretty good solution to this, potentially even for the logging use case. Classes (or code modules or whatever) only need to think about their direct dependencies, and indirect dependencies are handled naturally by the wiring code. In a well-written codebase using DI, you basically never need to write code that accepts an argument just so that it can pass that argument down to other code.
For example, if a class Foo deep in your program wants to use an interface called Logger for the first time, you just add a Logger as a field and pass the Logger into the Foo constructor in your wiring code. It's a little more ceremony than an import statement, but not by much, especially if you use a DI framework to do the wiring for you. Importantly, you don't need to make any changes to code that uses Foo.
An advantage to this approach is that it makes it easier to test usage of Logger (e.g. asserting that Foo logs an error in a particular situation). It also makes it easier to extend the Logger, like using a Logger wrapper that collects statistics on what was logged, without needing a special extensibility point in the Logger implementation.
That's not to say globals/singletons are always a bad idea. They tend to result in shorter code and they're easier to understand, and you can still test against them if you're willing to use mutable singletons (e.g. monkey patching in Python) or custom extensibility points. My main point is that, if you have a class that's useful in a wide variety of situations, there are other solutions than just "use a global" and "explicitly pass it around everywhere", and IMO dependency injection is one of the best options if you're writing serious code.
> because potentially you have to have an extra parameter to EVERY function or method
More often you have an extra parameter to class constructors, not every method. It's really not that bad, even when you choose to do it manually (as I do) rather than use a dependency injection framework.
> I believe in KISS - everything should be as simple as possible but not simpler.
I agree, which is why I avoid singletons because while they appear to reduce complexity in the short term they add horrendous amounts of complexity in the long term.
everybody user a Singleton some time in his life: cause it is often user as first pattern learned and also cause it is a quick and dirty workaround to fix Your code, For onstance to have a single connection handle to a database, instead of review your class diagram (if any).
I'm not sure how... pass it around is 'better' than global scope. Meaning not sure what problems you're avoiding here. If the scope is a single thread, no problem. If it's accessed from multiple threads, you have problems whether it's a global, singleton, or passed by reference.
Not actually completely true, you can still have races in single threaded code. Consider two state variables that are effected by a shared variable. Not hard to have the two state variables in disagreement over the shape of the world.
> I'm not sure how... pass it around is 'better' than global scope.
- Readability: You can see what components use the thing by following the variable as it is passed around.
- Testability: Tests can pass in a mock thing.
- Maintainability: If you discover someday that you need two different instances of the thing to pass into two different subsystems, you can do that. (This happens a lot, and programmers are really bad at foreseeing it.)
- Security: Only the components to which you've passed the thing can possibly use it (subject to the memory safety guarantees of your language).
Singletons are really just as bad as global variables. Why? Because they are global variables.