ColdFusion Singletons

Singletons are perhaps one of the most simple Design Patterns. For those who don't know sigletons are a class that can only have one instance. They can be thought of as a glorified global variable - but are a lot more useful.

Most ColdFusion classes, or rather instances of CF components, can be turned in a singleton by placing the following code in your Application.cfm:

view plain print about
1<cfif not structkeyexists(application,<instance name>)>
2        <cfset application.<instance name> = createobject("component",<path to component>)>
3    </cfif>

or OnApplicationStart method of your Application.cfc:

view plain print about
1<cfset application.<instance name> = createobject("component",<path to component>)>

The above code places an instance of the component in the application scope and you can then access the properties and methods of the component via the application variable.

Singletons can also be placed in other ColdFusion scopes such as the server or session scopes or even the request scope. Which scope you choose depends on what your code does.

Another way to create a singleton is to add an getInstance method to your component and use that to return the instance.

Like so:

view plain print about
1<cffunction name="getInstance" access="public" output="false">    
2
3        <cfif not isdefined("application.<instance name>")>
4            <cfset application.[<instance name>] = this>
5        </cfif>    
6        
7        <cfreturn application.[<instance name>]>
8    </cffunction>

Rather than hard coding the instance name we can base it on the displayname of the component.

view plain print about
1<cffunction name="getInstance" access="public" output="false">    
2        <cfset var displayname = getMetaData(this).displayname>
3    
4        <cfif not isdefined("application.#displayname#")>
5            <cfset application.[displayname] = this>
6        </cfif>    
7        
8        <cfreturn application.[displayname]>
9    </cffunction>

While this is an improvement on the original code this method would need to be added to all components you wanted to turn into a singleton. A better solution is to create a singleton component and in a component you need to turn into a singleton extend from the singleton component.

The singleton component (singleton.cfc):

view plain print about
1<cfcomponent displayname="singleton">
2    
3    <cffunction name="getInstance" access="public" output="false">    
4        <cfset var displayname = getMetaData(this).displayname>
5        
6        <cfif not isdefined("application.#displayname#")>
7            <cfset application[displayname] = this>
8        </cfif>    
9        
10        <cfreturn application[displayname]>
11    </cffunction>
12
13</cfcomponent>

The component we want to use as a singleton (dsn.cfc):

view plain print about
1<cfcomponent displayname="DSN" extends="singleton">
2    <cfset variables.DNS = "">
3    
4    <cffunction name="getDSN" access="public" returntype="string" output="false">
5        <cfreturn variables.DSN>
6    </cffunction>
7    
8    <cffunction name="setDSN" access="public" output="false">
9        <cfargument name="DSN" type="string" required="yes">
10        <cfset variables.DSN = arguments.DSN>
11    </cffunction>
12        
13</cfcomponent>

Using the component (in Applicaton.cfm):

view plain print about
1<cfscript>
2        if (not structkeyexists(application,'dsn')) {
3            application.dsn = createobject('component;,'dsn').getInstance();
4            application.dsn.setDSN('
mydsn');
5        }
6    </cfscript>

or OnApplicationStart method of Application.cfc:

view plain print about
1<cfscript>
2        application.dsn = createobject('component','dsn').getInstance();
3        application.dsn.setDSN('mydsn');    
4    
</cfscript>

In the page:

view plain print about
1<cfquery name="myquery" datasource="#applicaton.dsn.getDSN()#">
2    ...
3    </cfquery>

Related Blog Entries

TweetBacks
Comments (Comment Moderation is enabled. Your comment will not appear until approved.)
Just wanted to point out that typically, we prefer the 2nd method since the singleton would ensure there is only one instance of itself, rather than relying on the good sense of programmers to create only one of a particular type of object.
# Posted By Sammy Larbi | 4/10/07 2:29 AM
Since you're into design patterns, the technical name for the later version of singleton.cfc is a "factory", an object that exists to create other objects.

And while the sample code isn't bad, you'd have been better off creating a member variable within the factory itself to hold the instance you're creating. This is a good idea for two reasons:

1) One is to minimize side effects and avoid polluting the global application name space.

Someone maintaining the code may decide to create his own "application.dsn" variable, not realizing that deep within the bowels of your factory that application variable name is eventually going to get stepped on.

2) It makes it easier to "reset" your application when you make a code change.

The later point needs elaboration. Let's say your singleton facotry has grown to instantiate five different cached objects. With the method you've shown, that will also create five external application variables to hold them.

Now say you want to create a new kind of "dsn" object, that also has usernames and passwords. With your method you have to reinstantiate the factory, and also know that you need to zap the external application variable, since as far as the new factory knows application.dsn already exists.

If, however, the dsn object was stored within the factory in a member variable, reinstantiating the factory "resets" all of its internally cached objects as well, and as such new ones will be automatically created the next time they're requested.

In fact, if I were the aforementioned developer sent in to maintain your code, that's the behaviour I'd expect, in that once I recreate a factory it's going to return the proper results (e.g. new versions of new objects). By the way, the function name or hint attribute should always indicate that caching is occurring. (factory.getCachedDSN() or hint="Returns a cached DSN object, creating a new one if needed.")

All in all, manipulating external scopes breaks the "black box"; paradigm of an object being self-contained code and data, and generally should be avoided unless absolutely neccesary.

Good article otherwise.
# Posted By Michael Long | 4/10/07 6:22 AM
Hi Michael,

Thanks for the comments.

Not sure if it could be classed as a true factory even if it acts as one as it's only creating a single instance of one class, factories generally create multiple instances of multiple classes.

What I normally do is make a structure in the applications cope and all singletons are placed in that, that minimizes the issues of collisions. I omitted this from above code for simplicity sake.

The reason I have the method create the object in the application scope rather than doing it externally to the object is that a) it avoids coding errors and b) all singletons of this nature need to be in the application scope. I agree with you that generally it should be avoided and if this was Java or C++ there would be no need to do this. Possibly a more flexibly way would be to create an init method (as ColdFusion has no constructors) and pass the name of the variable you want to store the singleton in?

I also usually create a removeInstance method that does a structdelete on the instance variable for use for resetting. If there's enough interest I'll post the full code.

Justin
# Posted By Justin Mclean | 4/10/07 10:20 AM
Yeah, I realized the factory comments were a bit off about 6/10ths of a second after I hit post. Too bad there's no preview.

And I hope you don't mind, but I sort of took the singletons idea and ran with it. (http://cfinternals.typepad.com/blog/2007/04/when_i...)
# Posted By Michael Long | 4/10/07 11:25 AM
New improved code can be found here:
http://blog.classsoftware.com/index.cfm/2007/4/10/...
# Posted By Justin Mclean | 4/10/07 8:20 PM