To ensure that only one instance gets used, I have a public getInstance() method that calls a private init() method on the first instantiation. This sets an instance variable "instantiated" that gets checked on every method invocation and an error is thrown if that variable hasn't been set. So the only way to get a working object is to call getInstance(), which returns the existing singleton.
<cfcomponent name="LoggerManager" displayname="Logger Manager" hint="This CFC manages com.util.logger.Logger objects in application scope." output="false">
<cfset variables.registeredLoggers = structNew() /> <cfset variables.instantiated = false />
<!--- PUBLIC FUNCTIONS --->
<cffunction name="getInstance" returntype="com.util.logger.LoggerManager" output="false" hint="Returns a single instance of this object from Application scope.">
<cfargument name="reload" type="boolean" required="false" default="false" />
<cfif arguments.reload OR NOT (structKeyExists(Application, "LoggerManager") AND isObject(Application.loggerManager))>
<cflock name="App_LoggerManager" timeout="10" type="exclusive" throwontimeout="false">
<cfif arguments.reload OR NOT (structKeyExists(Application, "LoggerManager") AND isObject(Application.loggerManager))>
<cfset Application.loggerManager = init() />
</cfif>
</cflock>
</cfif>
<cfreturn Application.loggerManager />
</cffunction>
<cffunction name="addLogger" hint="Add a logger object with an automatically generated key, based on the path of the calling page. Any given MachII application should always be using the same index.cfm, so this should result in a single logger per MachII application." access="public" output="false" returntype="void">
<cfargument name="logger" required="true" type="com.util.logger.Logger" />
<cfargument name="overwrite" required="false" type="boolean" default="false" />
<cfset var local = structNew() />
<cfset local.currentLoggerKey = getFileFromPath(expandPath(".")) /><cfset checkInstantiation() />
<cflock name="App_LoggerManager" timeout="10" type="exclusive" throwontimeout="false">
<cfif arguments.overwrite OR NOT structKeyExists(variables.registeredLoggers, local.currentLoggerKey)>
<cfset variables.registeredLoggers[local.currentLoggerKey] = arguments.logger />
</cfif>
</cflock>
</cffunction>
<cffunction name="getLogger" hint="Get a logger. The logger that is returned depends on the path of the calling page." access="public" output="false" returntype="com.util.logger.Logger">
<cfset var local = structNew() />
<cfset local.currentLoggerKey = getFileFromPath(expandPath(".")) /><cfset checkInstantiation() />
<cflock name="App_LoggerManager" timeout="10" type="exclusive" throwontimeout="false">
<cfif NOT structKeyExists(variables.registeredLoggers, local.currentLoggerKey)>
<cfthrow type="LoggerManager.LoggerNotFoundException" message="No logger found for #local.currentLoggerKey#" />
</cflock>
</cfif>
<cfreturn variables.registeredLoggers[local.currentLoggerKey] /> </cffunction>
<!--- PRIVATE FUNCTIONS --->
<cffunction name="init" access="private" returntype="com.util.logger.LoggerManager" output="false">
<cfset variables.registeredLoggers = structNew() />
<cfset variables.instantiated = true />
<cfreturn this />
</cffunction>
<cffunction name="checkInstantiation" access="private" returntype="void" output="false">
<cfif NOT variables.instantiated>
<cfthrow type="LoggerManager.InstantiationException" message="LoggerManager must be instantiated with getInstance()." />
</cfif>
</cffunction>
</cfcomponent>
Christopher Bradford
----- Original Message ----- From: "Barney Boisvert" <[EMAIL PROTECTED]>
To: <[email protected]>
Sent: Friday, April 08, 2005 12:48 PM
Subject: Re: [CFCDev] Singleton in CF (Was: Where to put 'utility' functions?)
With CF and it's lack of static methods and fields, you can't really do an encapsulated singleton. The simple way with CF is to just instantiate your singleton in your application initialization code (which should never run more than once), and never do it again. The downside to that is that you have to instantiate EVERYTHING as part of app initialization, which isn't necessarily a good thing. Instead, you can instantiate a manager that you can request a singleton from. That manager ensures that it never creates two copies of a singleton, but you still have the problem of duplicate singletons if you somehow create duplicate managers.
I prefer the latter mechanism, in general, because I think it's cleaner.
cheers, barneyb
On Apr 8, 2005 8:54 AM, Ken Dunnington <[EMAIL PROTECTED]> wrote:So when you create your object, you have it check for an instance of itself in the local scope, and create a new one if one doesn't exist and pass it back? For some reason, it isn't quite 'clicking' with me... If you use createObject in your application to create an instance of the singleton class, how do you prevent another createObject call from creating another, unique instance of the class? Or can you only ensure there's only one instance per scope? Maybe it's the fact that CF makes us use a pseudo constructor that's throwing me off...
---------------------------------------------------------- You are subscribed to cfcdev. To unsubscribe, send an email to [email protected] with the words 'unsubscribe cfcdev' as the subject of the email.
CFCDev is run by CFCZone (www.cfczone.org) and supported by CFXHosting (www.cfxhosting.com).
An archive of the CFCDev list is available at www.mail-archive.com/[email protected]
