Justin Mclean at Class Software has just written a pretty good explanation of the Singleton design pattern. Unfortunately, while the explanation is great the code falls a bit short. Here's why...
To start with, let's first define exactly what a Singleton is. From Wikipedia, "In software engineering, the singleton pattern is a design pattern that is used to restrict instantiation of a class to one object."
Reading on, we find that "The singleton pattern is implemented by creating a class with a method that creates a new instance of the class if one does not exist. If an instance already exists, it simply returns a reference to that object. To make sure that the object cannot be instantiated any other way, the constructor is made either private or protected."
To see a good example of this, we need to go to Java:
public class SingletonObject
{
protected SingletonObject() { }
public static SingletonObject getSingletonObject()
{
if (ref == null)
ref = new SingletonObject();
return ref;
}
protected static SingletonObject ref;
}
Note that this fulfills all of the above criteria. The only way to get a Singleton object is to call getSingletonObject. In turn, getSingletonObject and its protected constructor ensures that there's one, and only one, copy.
Now, let's see the code at Class Software:
<cfcomponent displayname="singleton">
<cffunction name="getInstance" access="public" output="false">
<cfset var displayname = getMetaData(this).displayname>
<cfif not isdefined("application.#displayname#")>
<cfset application[displayname] = this>
</cfif>
<cfreturn application[displayname]>
</cffunction>
</cfcomponent>
Well, it looks like we have a function that we call to get an instance of singleton... but how do we call it? Since CFMX7 doesn't have static class methods or variables, the only way to call the method is to use CFINVOKE or createObject.
In either case, we have to create a new object before we can call its function to get an object. Or to put it another way, to get a copy to use we first have to make a copy, violating the singleton concept. Further, the component is not protected, meaning that we can call createObject("component"," singleton") all day long.
<cfset application.s1=createObject("component","singleton")>
<cfset application.s2=createObject("component","singleton")>
...
It only creates one applicaton variable, true, but that hardly matters since I can create as many copies of singleton as I want, all functional.
So how do we implement a true "there can be only one" singleton in straight ColdFusion? Well, the short answer is that you can't.
But you can come close. Let's start with the following:
<cfcomponent displayname="singleton" output="false"> <cffunction name="init" access="public" returntype="any"> <cfset var displayname = "_singleton"> <cfif structKeyExists(application,displayname)> <cfreturn application[displayname]> </cfif> <!--- do initialization, then... --> <cfset application[displayname]=this> <cfreturn this> </cffunction> </cfcomponent>
To use the component you'd do the following:
<cfset mySingleton=createObject("component","singleton").init()>
Note how the initialization routine checks for an existing object first, and if found, returns a reference to it instead. Only if it's not found does it initialize itself. Failure to use the resulting reference from init() means you'd end up with an uninitialized object, hardly useful.
This ensures that there's only one VALID object, with the obvious downside that, like the Class Software example, you're creating and throwing away objects. With that in mind, let's try adding a factory method to the mix:
<cffunction name="getSingleton" returntype="any">
<cfif not structKeyExists('application','_singleton')>
<cfset createObject("component","singleton").init()>
</cfif>
<cfreturn application._singleton>
</cffunction>
Now anyone who wants a singleton needs to call getSingleton(), which checks to see if one exists before creating it, eliminating the wasted duplication. We keep the protection code, since that's our guard against unwanted duplication. Further, the factory method hides the "use the reference from init" requirement, making the code more accessible to anyone else that may need to use it.
All in all, and like I said earlier, this is probably about as close as we're going to get.
Note that in all of the ColdFusion examples the object is creating an entry in the application namespace, otherwise known as a "side effect". While minor, it opens up the possibility of future bugs, should you or someone else maintaining your code unknowingly create an application variable of the same name.
A better approach would be to create a factory object... but that's best discussed another day.
Hi, 2 questions :
1.so, we have getSingleton() and init();
the idea is that first time initshould be called and afterwords getSingleton().. to minimize the uninitialized objects created ? .. because usually in OO PL the pattern is called with getInstance() all the time;
2.if it's used in the application scope .. and somebody is using it and somebody else want's it ..i suppose it has to wait for the other to end and then it will be available for him;don't you think that maybe it's better to use the session scope ?
Thank you
Posted by: mariusj | February 11, 2009 at 04:12 AM