ColdFusion Singletons Revisited

My last article on singletons got a few comments on blogs and in email including how it could be improved (thanks Michael) and a few questions. I also omitted the code showing how the singletons were created and called (now added).

So here's the new improved code!


Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Another issue with the Singleton factory you might find interesting.
# Posted By Michael Long | 4/12/07 9:48 PM
Yes sometimes a lock is required to prevent a race conditions from occurring but in this example it's not required.

A potential race condition exists with the above code, however it would only occur if the application variable doesn't exit and you get perfectly simultaneous requests. (ie once in a blue moon). Even if it does occur and two instances are created the same single instance is returned from the function as the application variable is returned rather than 'this'. ie the code works correctly.

For most requests (where the application variable exists) a race condition doesn't exist so adding a lock may be adversely affect performance by effectively making the code single threaded.
# Posted By Justin Mclean | 4/12/07 10:58 PM
The situation in your case would seem to be the same, just once removed, since both threads could have transitioned into the init method, and hit the 'if (not isdefined("application._singletons.#displayname#"))' test semi-simultaneously. If the first thread then blocked, the other could set "this" and exit, then the first thread could do the same. Dupe.

I also note that you added a "remove" method so the singleton could be recreated. Depending upon circumstances, the number of singletons, and how often that function is called, "blue moons" could occur more often that one might think, especially on a server under heavy load.

All in all, I think it's best to program defensively, and sinply ensure that the bug never has a chance to appear. And since the lock is inside the if test and should only occur once on object creation, the performance aspect is negligible.

I did like deleting the function reference. Neat trick. Adding function references is fun too...
# Posted By Michael Long | 4/12/07 11:40 PM
Ummm... I may be missing someting, but if you delete the object's "invalid" function in the getInstance() factory method and call init(), isn't the init() function going to blow up with a "function not found" error when it hits this.invalid()? I mean, deleting the function doesn't delete the call to the function.

Instead of deleting it wouldn't you need to do something like: instance.invalid=instance.valid; setting the "invalid" function to a safe one so initialization can proceed to completion?
# Posted By Michael Long | 4/13/07 12:09 AM
Understand what your saying but even if it does occur in my code it's not actually a problem as the same instance will be returned. Yes two instances are created but the same one is returned in each case.

If you can do the locking in such as way that it doesn't have an effect on performance then yes it should be added. It may be that later on changes to the code does make the race condition have negative side effects and these issues are very hard to debug/track down.

Glad you liked the removal of methods via structdelete still not 100% sure I'd actually use it in a production system but I'm sure it could be useful under other circumstances.
# Posted By Justin Mclean | 4/13/07 12:29 AM
You would think it would throw an error wouldn't you, however I've tested the code and no error occurs, it's like CF removes the method but not the fact that it existed. If you call getMetaData the method is not listed.

Originally I was checking to see if the method existed before calling it but once I found it wasn't needed I removed the checks to make the code simpler.
# Posted By Justin Mclean | 4/13/07 12:31 AM
I do not think you can "enforce" many of the design patterns in CF (singletons included). Having said that your code is quite elegant but you are not creating a singleton, you are creating a "static" class. Your class is instantiated for ALL USERS and not 1 stances PER USER.
Now you might say ... just scope the class to SESSION... but not really a way to go in my books.
# Posted By AAP | 6/27/08 2:20 AM
Yes without private constructors you can't make a 100% foolproof way of creating a singleton in CF, but you write one that behaves like one as long as you play nicely with it.

Generally I place the instance in the application scope that way it a singleton for the entire application. If it was placed in the session scope it would be a singleton for that users session, which can also be useful in some situations.
# Posted By Justin Mclean | 6/29/08 12:25 PM