From STUPID to SOLID Code!
williamdurand.fr
williamdurand.fr
The only time I ever use them is when my hands are tied by some sort of hardware restriction; a JNI wrapper around some native lib that talks to proprietary hardware like a motor controller where instantiation of more than one comms object would blow out a fuse or something. And even then, I'd make the argument that the people who designed the hardware and its corresponding C API should have just made a safer interface instead of saying "Don't call `new` more than once, or else!". Seems lazy and less-than-appropriately fault tolerant.
Like anything, it's a tradeoff. Let's say I have an app with 10 OpenGL windows, and I want to simultaneously control the lighting model for all at once. A singleton gives me an easy way to do that, but then the windows have to poll in some manner (which is perhaps not an issue, since they are in a render loop anyway). But I am brittle - introduce the requirement that each window handles light differently and the singleton is toast. OTOH, introducing a signal/slot or model/view architecture may be overkill for what I am doing. This might be a small corner of the code, and I just want clarity for the few times I go in to read it. I find it extraordinarily hard to understand messaging code - where did this message come from, what was the state of that object that caused it to fire the message, and so on. I think complexity goes up at least by 1 order of magnitude when you have to maintain state and time in your mind to understand a program.
Perhaps not the best example; so consider command line arguments. It's read only, set once, but perhaps needed to be accessed by many different parts of my code. A singleton or 5 seems pretty darn clean to me. Again, not without it's tradeoffs, but the alternatives, to me, seem worse. You can pass, pass, pass, the data through methods and constructors until it gets to the code that needs the info - in which case you have infected a lot of code with rather superfluous (to them) knowledge about command line settings. That's brittle, and you end up touching a lot of code when you add something to the command line. You can do a signal/slot or model/view, which can be quite powerful, but you may be forced into a specific design by your sw stack (they have a model/view architecture which you are using for GUI or something). So your code is brittle by being coded to a specific API, and again, it rather obscures where this data comes from. Plus, this forces the command line reading code to know all about your specific API, how to format the signal, when to send it (command line reading is often done well before the whole stack is started up and going, so you can't send a signal yet). That's very brittle as well. Can you take that code, put it in a test harness, and trivially write tests for it? Probably not.
Maybe I am missing some obvious technique. But a few things in an app are in fact global. Trying to obscure that fact, to my mind, just makes the sw more brittle and a lot harder to understand and debug. Which is not in any way an argument for making non-global things global for convenience.
Sure, but for the most part, there is no reason for the components of the app to depend on those things being global (and a significant cost in brittleness for them to do so), so, in general, they should be passed to the objects that need them, even if they are globally consistent throughout the app.
Obviously, there are optimization reasons why you might need to avoid doing this in particular cases, but in general doing so up-front is going to be a premature optimization.
BINGO! This is the anti-pattern part. Not that the data is globally required, but that your units are mutually dependable. This is why Interfaces and Dependency Injection are so popular now, because they facilitate the ability to make easily testable, singular units that allow for mocks / stubs etc.
To illustrate, what happens if your global requirement happens to also be external? Would you want to have to test your component with a live version of your global and external resource? No, you would want the ability to test (either as a single unit, or even the whole app), without requiring the database, messaging, web service, etc, to be up.
But the majority of singletons I have seen in practice (maybe my bad luck) contain mutable state. In the almost two decades I have been programming I have never come across a situation where I needed a singleton with mutable state. But hey I have not seen it all.
Programs need to be interpreted by a computer, and as such need to be written in a formal language everywhere, and as such has a larger amount of concepts that need to be formally modelled. More concepts means we need more descriptive names to be able to distinguish them.
Programming by its nature is an exercise in re-enventing the wheel (how many different semantics are there for opening a file in all the different languages in use?). Also, the sheer number of algorithms and abstractions in use in programming dwarfs that of mathematics.
Most of conventions used in math come from the time before computers; on paper, every character you don't have to write by hand is a big time-saver.
Moreover, math papers tend to be read top-down. You start at the beginning, and gradually build your understanding and "symbol-to-concept" mapping table as you go down. Whereas in code, you often just jump in the middle of a file to change something, and you don't want to read 1k lines of code in some random place to determine what f(a,b,q(z)) means. Descriptive names allow you to have all the context you need inferable locally.
Note that many good principles of programming are exercises in maintaining locality. Reducing program state, functional programming style (function output depends only on inputs, not on hidden state), writing small functions, building abstraction layers (treating one layer as a "programming language" for the layer above) - all of these allow you to understand and change the code you're working on without having to look at unrelated things.
Because context-switching and then processing concise symbols in that context is more efficient for most people's brains that processing prose.
Is it really better for me to pass the Clock instance all over the place, rather than have a singleton instance that can be referenced anywhere?
You can then have two much simpler clock classes, one for real time and one for fake time.
Personally, I prefer just passing around the clock, though - it makes dependencies between classes crystal clear.
I believe that we should be able to come up with a set of (slightly different) rules that are simpler to explain and yield the same good designs.
-- edit Wikipedia: If Square and Rectangle had only getter methods (i.e., they were immutable objects), then no violation of LSP could occur.
Yeah, this is the fundamental problem; mutable objects tend to result in LSP violations when class heirarchies follow common sense is-a relationships, but mutating operations are not limited to those (if any exist) that preserve identity.
A corollary to this is that the use of default constructors very often accompanies LSP violations, as it usually implies all of the object's state is subject to mutating operations.
Also, both these acronyms seem to lean toward the object-oriented paradigm. I'd like to see something more universal.
The way I see it, it couples objects to the things they should be coupled to while avoiding unnecessary coupling.
This is an interesting claim. Could you explain further how this is the case (or provide references which explain it?)