They are generally misapplied by developers as a sort of global state, usually with the implied belief that "I'll only ever need one of these".
This has all kinds of nasty downstream effects. It becomes more difficult to trace the dependencies between classes, since any class can invisibly depend on a singleton. It becomes painful/impossible to replace that dependency in a particular instance, which makes both testing and extension difficult.
Dependency injection solves the issue of sharing copies of the same object, without forcing that the object is implemented as a singleton. You can inject a different/extended implementation, and it becomes easy to trace dependencies.
How does it make it harder to trace class dependencies? Find all references to the singleton.
Sometimes you just really need some global state. Have you ever done game development? Unless you want to pass a giant GameState godobject to every single thing. Singletons or static classes: NotificationManager.PushNotification(..), DBManager.Instance.UpdatePosition(..)
Singletons are good for when you want a static class but also need some things you can only do with instance classes.
Singletons prevent you from running more than one instance of an app inside a single VM/interpreter, in those kinds of languages. They're marginally useful if you don't care about those situations, but honestly it's never been worth it to me.
I've actually done a lot of "game" development, both as a hobby and in non-game-development fields (lite simulation work increasingly resembles games, and I think in general they should be developed in the same way).
Static classes are a hack for languages that force everything to be an object. You don't see static classes in languages like C++ or Common Lisp or JavaScript. You pretty much only see it in Java or C#. You make global (though certainly namespaced, we don't need to be colliding names here) functions instead.
And the difference between instance classes and static classes is that instance classes encapsulate state. So a singleton is literally nothing more than stateful global functions.
The realization you're starting to need a GameState godobject is what is called a code-smell, i.e. trying to find answers to the wrong problem of "how do we get all of this state around?" The problem should more correctly be "how do we avoid needing to have so much state passed around?"
It's usually a sign that the project is using too much inheritance. It seems natural to have a class "ProjectileWeapon" that has a virtual "fire()" method that returns a "Projectile", from which "Gun" inherits and overrides fire() to return a "Bullet" and "RocketLauncher" overrides to return "Rocket". But the Bullet only has a momentum vector, the Rocket also has fuel, and the Bullet only does kinetic damage, the Rocket includes chemical, and different materials are more or less resistant to different types of damage. The interactions between types that all try to encapsulate their own behavior starts to multiple the number of cases where different bits of code need to know about different parts of the world.
It at first seems to reflect the real world, but in reality a Rocket knows nothing about the air it is flying in and the objects it hits and blows up on. And those objects don't know anything about the Rocket, either, they are equally a'splodey whether they're hit by a Rocket or a Grenade.
The better way is Composition, the "has-a" in "is-a" versus "has-a". A Rocket has fuel. You don't say a rocket "is a fueled thing". A physics engine takes all of the things that have fuel and the things that require fuel (rocket engine) and asks "what is your fuel flow rate" and "what is your fuel consumption rate", respectively. Interfaces and Mix-Ins can be exploited to do this in a type-safe way.
Inheritance is useful when a strict tree-structure can appropriately encapsulate your needs. Unfortunately, very few things are strictly tree-like. The only times I've found a strictly tree-structured problem in the last 15 years has always involved processing language grammars. Inheritance works fine there. But even still, Haskell-style pattern matching works better. I won't go so far to say "inheritance is useless", but I will say my personal experience has been that I've learned to regret having used it in every instance.
>The problem should more correctly be "how do we avoid needing to have so much state passed around?"
I work on a large game, and multiple smaller games of my own, which all use a few static classes or singletons. It's never caused an issue, and I've never had such a problem with inheritance. I've never regretted using it in my code. What about Exceptions?
You don't seem to have offered a way to avoid having so much state to pass around. Where does the notification queue go? Where do the settings go? Where does the scene manager go? All the MVC controllers I have are static or singletons.
I don't see what objects have to do with no global variables or functions being allowed. There's no theoretical reason a global function can't operate on or return objects.
>> There's no theoretical reason a global function can't operate on or return objects.
That's not the same thing as stateful at all. "function add(a, b) { return a + b; }" operates on two objects and returns an object, but it maintains no state. I said global state was a bad thing, not global functionality. I added a caveat that global functionality should at least be namespaced, though.
You avoid passing state around by either injecting the dependencies or returning the state transitions to some other thing that makes the state changes happen. You make explicit the relationships that the singletons hide.
In may frameworks service layers are usually implemented as singletons to prevent the expansion of memory requirements with a lot of throughput. However, services designed like this _should_ be stateless. Basically they exist to do the heavy lifting of data transformation. There no real problem with this.
The problem comes when you mix state into a singleton. State-full singletons are basically global variables. Even if you design all your methods as transactional/synchronized/blah/blah you will run into issue where the state of the singleton isn't what you expected because it was changed via a different method/thread/blah/blah.
Basically this a school of hard knocks thing. They're great, until you have to debug it in production.
Could you expand on that? Haven't heard this argument before