Hacker News new | ask | show | jobs
by colin_mccabe 3991 days ago
It's funny how you can take an obvious antipattern (global variables) and turn it into a pattern by giving it a cool new name like "Singleton". In this spirit, I propose "the ProgramCounterAmbulator" as a cool new name for "goto."

Actually, gotos are usually less harmful than globals. At least they don't interfere with unit testing the way globals tend to.

2 comments

No argument that Singletons make it difficult to unit test things but there are genuine cases where singletons are required: For instance, you don't want to end up creating multiple objects that read the configuration file, when one is enough. Or you certainly don't want to create too many objs that are heavy (like a cache that stores data heavy objects, or services, or god-objects (which are themselves an anti-pattern unavoidable in certain cases)).

At times, there's genuinely only a single entity of "x" that's available for use by the environment, like a security-policy, or access to standard-output, and so on...

Singletons are necessary evil, IMO.

Re: Global state: At some point the abstraction will have to leak. If you squint enough, nothing is truly isolated, and everything's sharing everything else with other binaries on any given system at some abstraction level or the other. This is more often the reason why security-exploits are theoretically possible despite isolation.

Depends on what you mean by "singleton". If you define it loosely as "a type of which there is only one instance", fine, no problem -- of course those will exist. But if you define it as "globally-accessible mutable state", then the problems start. Usually, the "singleton pattern" is explicitly a technique for doing the latter.

> For instance, you don't want to end up creating multiple objects that read the configuration file, when one is enough.

Sure. But that doesn't mean the config file reader has to be globally-accessible. Instead, try allocating it on the stack in your main() function, then passing the object into each component that needs to see it. Better yet, only pass each component a sub-object(s) of the config which applies specifically to that component.

Now you have a bunch of useful benefits:

- Readability: You can clearly see and follow what components are affected by what parts of the config file.

- Testability: Unit tests can easily provide a test configuration.

- Maintainability: If some day you realize that you need to create two instances of some component and configure them differently, it's easy to do that without rewriting tons of code or introducing horrible "namespacing" hacks.

- Security: If your config file contains anything sensitive (say, database credentials), it's no longer the case that every damned module in the whole system has the ability to read those secrets. In fact, if your language is memory-safe and bans mutable global state, you can trivially sandbox any piece of code by simply not passing it references to anything it shouldn't be able to access. (This is called "capability-based security" or "object capabilities", and it works.)

Extended argument (which I wrote many years ago...):

http://www.object-oriented-security.org/lets-argue/singleton...

Nailed it. It's fine to use a singleton, but don't call it from within every class you use it in. Just pass a reference to the instance of the singleton. Call it once in the outermost scope.
Just wondering what value the singleton provided if you only obtain it once. Isn't the purpose so you can obtain it whenever you want? If you just use a regular object, you run the risk of accidentally making another instance somewhere else, but you always have that risk with any regular object anyway.
It's kind of OK to use, as long as you don't call it all over the place and render your code untestable such that it can't be mocked out, but I'm not a huge fan of it. It's really a matter of opinion at that point.
I find that a better approach is to try and isolate pieces of code that needs access to specific resources, such as configuration files or standard output. Performing IO tasks, from every module in a code base, is an easy way to create very unmaintainable code.

Of course this is all a tradeoff with other code structuring ideals. Programming languages with Monads tend to be very good at isolating such things.

Any discussion of Singletons is incomplete without https://sites.google.com/site/steveyegge2/singleton-consider...