8 ms·
The Case for Singletons
- bcoates 14y agoNecessary singletons are a sign that your language/library either embraces "everything is a class" so you have to make vacuous singleton classes to get full access to the language, or it is lacking a sufficiently powerful module system so you have to implement an ad-hoc one yourself using classes. Unnecessary singletons are usually a sign that someone, somewhere designed a way too general system that allowed multiple instances of something, then nobody ever used that feature, then someone else came in and made things a bit saner by adding a singleton constraint, leaving vestigial object-ness and meaningless pointers being passed around everywhere. In a perfect world with perfect programmers you'd never see one so the instinct that they smell bad is correct.
- masklinn 14y ago> Unnecessary singletons are usually a sign that someone, somewhere designed a way too general system that allowed multiple instances of something, then nobody ever used that feature, then someone else came in and made things a bit saner by adding a singleton constraint I fail to see how that makes anything saner.
- bcoates 14y agoIf your program has an unused and untested capability, killing it is a move in the right direction even if it makes your program less elegant. Bad behavior is worse than ugly design. Of course the right thing to do is to simplify the rest of the program to take advantage of the new assumption but maintenance programmers don't always have the luxury.
- mikevm 14y agoIf we're on the subject of anti-patterns, should GOTO really never be used, in any situation? I remember writing some code in C a few years ago and I needed a place in a function where cleanup is performed, and in case there were errors caught in multiple places before you get to the cleanup area, I used a goto statement to jump there after printing the error message and turning on a special error flag. How would you solve that problem without goto?
- bcoates 14y agoWithout goto, you'd either introduce a bunch of unnecessary functions or do this: init; do { stuff; if (fail) continue; morestuff; } while (0); cleanup; Which I've seen in real code released to the public. Whoever does either of those things instead of just using a goto should be flogged in the town square. edit: Other usually-bad alternatives to goto are the pyramid-of-nested-ifs or a set of running "bad" flags/enum that disables each operation downstream of the error until the cleanup is reached.
- IsaacSchlueter 14y agoYou would put the cleanup code in a separate function, and call it. I don't think there's anything wrong with using goto in such a case, though, if it makes the code more understandable.
- humbledrone 14y agoJust putting the cleanup code in a separate function doesn't solve the problem. In C, goto can be used as a kind of fall-through mechanism to clean up all of the resources a function has acquired (but no more!), in the opposite order that they were acquired. There really is no other way to accomplish this without horrible if/else nesting, which for complex functions would be pretty ridiculous. [1] http://eli.thegreenplace.net/2009/04/27/using-goto-for-error-handling-in-c/ http://eli.thegreenplace.net/2009/04/27/using-goto-for-error...
- asveikau 14y agoHaving worked in a large C code base that was occasionally tainted with this approach (rooted only because people have been told to avoid goto as dogma), I have to say that goto is a better approach to emulate try-finally or RAII in C. For one, the goto will read better as a single piece of prose, there will be no need to glance over at some other function to see the free call. Second, let's say your function has 2 allocations and a lock acquisition - that means your cleanup function needs a pointer to each, usually done with parameters. Add an extra allocation? New parameter to the cleanup function. This gets messy, fast, in a way that the goto does not.
- 14y ago
- rdwallis 14y agoSingletons are easy to implement, simple to understand and have no performance penalties. They don't need to be defended. If they work for your project, use them.
- masklinn 14y ago> Singletons are easy to implement, simple to understand and have no performance penalties. Unless there's any concurrency to the system, that is. Then they're shared state (as fraught with peril as any shared state) except they're shared state which can be broken/corrupted straight from the initialization.
- VMG 14y ago$this->persist That's just sad. And the author even is a wordpress developer
- eamann 14y agoYes, it's sad. And no, I'm not a "wordpress" developer, I'm a WordPress developer who didn't properly proofread everything before publishing. You should probably take a lesson from that ... :)
- seanalltogether 14y agoI can't speak to server side development, but for ui applications, storing your models in a singleton can save you a lot of time. When you're developing apps its very easy to get stuck in a mindset that you only need certain data for each screen, but the truth is the data for one area will inevitably bleed over into other areas, and having a good way of updating and reflecting data to the areas needed is the sign of good architecture.
- api 14y agoI've been of the opinion for a while that static variables of any kind should be avoided except for const data (tables, constants, etc.). They make code very difficult to impossible to port to multi-threaded environments, and make it hard to cleanly instantiate and then clean up a module within a larger program. Even things that feel like they should be global should be contained with a global context container that can be cleaned up.
- eamann 14y agoI agree. But remember, the article is specifically about singletons in WordPress, which is written in PHP, which is single-threaded.
- masklinn 14y ago> the article is specifically about singletons in WordPress Is that why you open with > I would argue that Singletons are not just useful, but are necessary for robust software development. ? No mention of wordpress there, you use it as an example but your assertion is that singletons are downright necessary to software development.
- mikeschinkel 14y ago@eamann, I gotta give @masklinn credit there, you didn't explicitly make it clear your post was focused on WordPress-related use-cases. It was obvious to me from your references and knowing your focus but maybe not obvious to those who don't specialize in WordPress if all they did was scan the article, as most people do.
- masklinn 14y agoNo, even reading the post it's not obvious. It's obvious that he talks about wordpress, but considering the assertion I quoted I simply read it as using a wordpress-based example/use case to support his hypothesis. At no point did I find or notice a modifier of the article being an argument for the usage of singletons in wordpress plugins. though I may just have missed it.
- pcwalton 14y agoI generally prefer Lisp-style ✲dynamic✲ variables. They basically work the same way singletons do, but they can be set and reset at a block-scoped level (great for test mocks and the like, and makes your control flow easier to understand), and they play much better with threads. Rust doesn't even have singletons or global variables, since they aren't threadsafe. It has thread-local storage, which dynamic variables are built on top of.
- masklinn 14y ago> It has thread-local storage, which dynamic variables are built on top of. That hack only works if execution can never be suspended though. The issue exists in Python where it's possible to fake dynamic scope using threadlocals, but that can blow up if execution is suspended (between `yield`s in a generator, for instance) as the stack doesn't get unwound (merely frozen) and the threadlocal isn't reverted if it's in the frozen part of the stack. Same deal if it's possible to unshift stack frames, the threadlocal will more than likely end up in the wrong state. An other thing which bothers me with threadlocals is that it's not really possible to introspect currently-bound locals in order to "save" them alongside lambdas (though that may be an API issue more than an issue with the locals themselves), and in my experience async callbacks tend to need their creation stack context more than their callstack context.
- pcwalton 14y agoBoth good points. Rust doesn't have the problem with execution being suspended, since the only mechanism for suspending execution is through the task system. Thread locals are all per-task. The issue with introspection is fair, and it's a gotcha you have to keep in mind whenever you use dynamic variables. They're necessary to make the task system and condition handler system work under the hood, however, so we can't just remove them.
- masklinn 14y ago> They're necessary to make the task system and condition handler system work under the hood, however, so we can't just remove them. Oh I've nothing against dynamic variables at all and wouldn't want them (or conditions) to be removed from Rust (in fact I wish solid dynamic variables were available in more languages), don't get me wrong.
- wvenable 14y agoI prefer to mix both dependency injection and singletons for the best of both words. Classes accept dependencies in their constructors but as optional parameters -- if the parameter isn't provided, a default reference is pulled from a singleton. It saves a lot of extra typing when 99% of the time your dependencies are fixed.
- wvs 14y agoThe main issue I have with singletons is that any mutable global state prevents your application from being first-class within its own runtime environment. This is of course useful for unit testing, but also reduces hidden state dependencies between submodules.
- michaelfeathers 14y agoThere is another argument against singletons. They destroy design information: http://michaelfeathers.typepad.com/michael_feathers_blog/2012/12/global-variables-destroy-design-information.html http://michaelfeathers.typepad.com/michael_feathers_blog/201...
- jcampbell1 14y agoI am not a big fan of "getInstance". For a session it should be named something like "defaultSession". It is perfectly reasonable that you later want to add a separate session that requires HTTPS only cookies, in which case it could be called "secureSession".
- zaphar 14y agoIs it just me or did he never actually address the dependency injection complaints? It read like he spoke glowingly of how dependency injection decouples your code and makes your blissfully flexible and then nothing. Was his counter argument just that sometimes you need to be less flexible?
- jasonlotito 14y agoHe tried to, with the suggestion that with DI, you suddenly had the dependence hard coded and would have to make a code change to change the dependency. This, of course, is not accurate.
- jessedhillon 14y agoDownvote away -- I know I deserve it, but I need to say this: Whenever I see a discussion of good software design principles given by a PHP programmer, with PHP examples, I roll my eyes and press Back. Rightly or wrongly, the thought that goes through my head as a former PHP programmer (from versions 3 - 5.x, ending in ~2010) is something like, "if this person knew anything about well-designed software, they'd know enough to use a well-designed language."
- eamann 14y agoNope, I'll upvote :-) I started my career as a .Net developer and still work a great deal with .Net (primarily C#) code. I use PHP for work because that's what WordPress is built on and, without rewriting the platform that powers a huge chunk of the Internet, I have to use it. When in Rome ... And I never said singletons were a good software design principal - just that they have a place and a purpose (even if rarely).
- jessedhillon 14y agoRegarding specifically WP, which is how I made ends meet (and then more) for a number of years -- it's like crack, you hate it and you know you're better than this, but it's just makes life so easy if you don't resist. Not a perfect analogy, but what I'm trying to say is that, yes it's easy and you're fighting the uphill battle if you don't go with it. Plus, WP consulting can be very profitable, given how much of your code can be reused from project to project. But, I have to say, at this time the flaws of WP are so well-known, and the direction and style of the WP devs are such a known quantity, that we as users/devs know what it is and we know exactly how good/bad it's going to be. It's never going to be a well-designed MVC application, and it's probably never going to conform to good OOP design either. It will probably continue to look pretty nice on the backend, but remain really annoying to extend for anyone but the greenest noobs. And it will probably have a ton of features which seem like bloat to people who simply want a blog, and like cruft to people who want a full-fledged CMS. I think, after so many years, the cost of building a reusable system from scratch is starting to outweigh the benefit of just sticking with WP. One of the major pillars supporting WP's proliferation is the ease with which one can find hosting -- just upload your site, if it doesn't have push-button install already -- and I think the ease with which a Python or Ruby app can be deployed has now matched that. At least for a certain range of users.
- KirinDave 14y agoSo Eric is sort of right although he doesn't explicitly state it: Singletons may provide a decoupling point nearly identical to dependency injection when designed with care. But that's not really the problem, and he stops with an argument that is compelling to someone in 2002 but not so compelling for someone from 2012. The reason we prefer non-singletons to singletons are as follows: • Most languages do not provide very good ways to prevent singleton collision. As software has become more complex and open source's success increases the size of the average dependency tree, this problem is exacerbated. • Singletons force you to confront all the problems shared state concurrency has to offer, with almost no support. DI objects tend to be isolated from one another and do not run into that level of complexity so soon. • Singletons tend to make it harder to test code unless your language has a method for overriding or mocking singletons within a dynamic scope (or static type checking between types with liskov-substitutable signatures). The most classic exampe of this is one of the most prevalent singletons: your system clock. Knowing what we know now about how successful software projects tend to be confronted with expectations of concurrency, parallelism, robustness in the face of reuse, and testability; singletons offer a lot of costs for very transient benefits.
- masklinn 14y agoSingletons also make things painful if (when?) you have to go from "one instance" to "two instances", which regularly happens.
- eamann 14y agoWhich is why they should be used sparingly, if at all. I'm not advocating we go out and start making every class a singleton, just explaining how they do serve a purpose in PHP/WordPress development.
- mikeschinkel 14y agoI support Eric here. Singleton make sense in WordPress specifically for encapsulating plugins. In WordPress plugins can never have more than one instance so it's really a moot point to expect them to be anything else. Outside of WordPress plugins, I don't currently know of cases where I'd also advocate for singletons. Yes in most other cases singletons are bad but I'm sure there are some other cases where they do make sense. And you can't prove a negative.
- debacle 14y ago> Think of every time you’ve used the global keyword when working in WordPress. I pretty much stopped reading there. Referencing WordPress when making a point about software design does not strengthen your point. Singletons are the antithesis of dependency injection.
- georgemcbay 14y agoMy problem with most singletons is that they are a half-way solution for a developer unwilling to commit and accept that sometimes a bit of global state isn't the end of the world. Static fields and static methods on classes should be used sparingly, but if you're going to use them, just use them. Why half-assedly try to hide the fact that you're using them by wrapping them into a singleton? This is a(nother) thing I like about Go, the design of the language allows for the fact that sometimes a function is just a function and doesn't need an object receiver and maybe it even modifies a package level global, so what? Just make sure you properly handle multithreading situations. In Go code you don't find a lot of patterns where people are trying to sweep what they are doing under a rug in order to not anger people who accept "common wisdom" code design guidelines as absolutes. If you need to write "dangerous" code (and sometimes it is best to write "dangerous code"), do it in the open, out in the light of day, that way at least it can more easily be reasoned about if someone does hit a snag in it.
- mikeschinkel 14y ago"Why half-assedly try to hide the fact that you're using them by wrapping them into a singleton?" I may be misunderstanding your context for the above comment but when I use singleton classes for WordPress plugins I do so to avoid potential namespace collisions with other WordPress plugins.
- cousin_it 14y agoSingletons, globals/statics, and dependency injection all seem to be solutions to the problem of making objects find each other at runtime. Maybe it's time for a dedicated language feature to solve that problem? For example, each object could carry an implicit reference to an "object pool", from which it can fetch other objects by their class/interface. If an object constructs other objects, the "pool" gets passed along implicitly. Test suites could use "pools" filled with mocks. And unless I'm missing something, the presence of all required dependencies could be statically checked at compile time. Basically it would be like Lisp's dynamic variables, but based on the construction hierarchy instead of the call hierarchy. What do you think?
- 1010011010 14y agoThis is a good read on singletons. http://www.object-oriented-security.org/lets-argue/singletons http://www.object-oriented-security.org/lets-argue/singleton... "Singletons are possibly the single greatest obstacle to good object-oriented design today. They are often at the root of huge intractable design flaws in large systems, and yet many programmers continue to use them and even insist that they are a good design pattern. Many programmers point to the fact that the singleton pattern is a prominent part of the book Design Patterns, but don't realize that at least one of the authors of that book (Erich Gamma) is on record saying that it shouldn't have been. We argue that Singletons are almost always bad design and almost never needed. In the few cases where singletons seem necessary, the "environment pattern" offers a reasonable alternative. Therefore, we recommend that programmers always avoid singletons and that programming languages ban them or at least provide an option to ban them."
- 1010011010 14y agoThe number of replies defending PHP make me think that HN is very close to becoming a useless place, dangerous even, for obtaining programming (and probably other) advice.
- eamann 14y agoIt's not about defending PHP. PHP is an ugly, inconsistent language. It's a matter of pointing out why certain tools are used in certain environments.
- eamann 14y agoI've posted a follow-up to this that broadens the ideas of Singletons for general PHP use. Using abstract classes and inheritance, it's also possible to unit test the code contained within a Singleton. The follow-up is here: http://news.ycombinator.com/item?id=5023027 http://news.ycombinator.com/item?id=5023027