| 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]. [1] - http://stackoverflow.com/questions/228164/on-design-patterns... [2] - http://www.reddit.com/r/programming/comments/197dn1/introduc... |
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.