A Coldfusion Transient Factory Example

When learning how to apply object oriented principles to ColdFusion development, one will assuredly encounter the subject of design patterns. Singleton, Factory, Strategy and Bridge are just some of the patterns that describe solutions to common problems in programming. For this example, I'll demonstrate a variation on the Factory Pattern for transient objects. What is a factory? At it's simplest, a factory encapsulates the creation of objects to provide a consistent api for clients. What is a transient object? A typical OO ColdFusion application will incorporate a combination of Singletons (one instance per application - often instantiated at application startup) and Transients (a per request object that is not maintained in a persistent scope).For instance, when invoking a "save" method on a service, I return a new "result" object. Therefore, each of my services needs to create a new instance of a result object. Rather than place CreateObject("component","path.to.Result") in all services, I use a generic factory pre-configured with object aliases and class paths. I can then use: <cfset result = TransientFactory.create("Result")> But what if the object has setter dependencies or an init() method with constructor args? For any setter dependencies, I incorporate Brian Kotek's beanInjector within the TransientFactory to autowire all matching ColdSpring managed beans. For consturctor args, there are two ways to pass arguments to an init() method. 1. passing a struct of key/value pairs as a second argument to create(). <cfset initArgs = StructNew()>
<cfset initArgs.myprop = "myvalue">
<cfset result = TransientFactory.create("Result",initArgs)>
2. using the virtual method new{objectName}. Transientfactory.newResult(argumentCollection=initArgs). <cfset result = TransientFactory.newResult(myprop="myvalue")> The second method of object creation is made possible by onMissingMethod. Additionally, you can specify the name of a method that the transient factory will invoke after initialization and setter dependency resolution. Here's a simple example for setting up the TransientFactory in ColdSpring. If you use some of Brian Kotek's excellent ColdSpring Bean Utils, you notice a reference to his BeanInjector component. This TransientFactory example is similar to Brian's TDOBeanInjectorObserver component that autowires ColdSpring managed singletons into transfer objects. <beans>
<bean id="beanInjector" class="model.BeanInjector" singleton="true" />

<bean id="TransientFactory" class="model.TransientFactory" singleton="true">
<constructor-arg name="classes">
<map>
<entry key="ServiceResult">
<value>model.util.ServiceResult</value>
</entry>
<entry key="Timer">
<value>model.util.Timer</value>
</entry>
</map>
</constructor-arg>
<constructor-arg name="afterCreateMethod">
<value>setup</value>
</constructor-arg>
<property name="beanInjector">
<ref bean="beanInjector" />
</property>
</bean>
</beans>
Here is the full TransientFactory component code. <cfcomponent displayname="TransientFactory" output="false" hint="I create Transient objects.">

<!--- public --->

<cffunction name="init" access="public" output="false" returntype="any" hint="returns a configured transient factory">
<cfargument name="classes" type="struct" required="false" default="#StructNew()#">
<cfargument name="afterCreateMethod" type="string" required="false" default="">
<cfset variables.classes = arguments.classes>
<cfset variables.afterCreateMethod = arguments.afterCreateMethod>
<cfreturn this />
</cffunction>

<cffunction name="create" access="public" output="false" returntype="any" hint="returns a configured, autowired transient">
<cfargument name="transientName" type="string" required="true">
<cfargument name="initArgs" type="struct" required="false" default="#structNew()#">
<cfset var obj = createObject("component",getClassPath(arguments.transientName))>
<cfif StructKeyExists(obj,"init")>
<cfinvoke component="#obj#" method="init" argumentcollection="#initArgs#" />
</cfif>
<cfset getBeanInjector().autowire(targetComponent=obj, targetComponentTypeName=arguments.transientName) />
<cfif StructKeyExists(obj,variables.afterCreateMethod)>
<cfinvoke component="#obj#" method="#variables.afterCreateMethod#" />
</cfif>
<cfreturn obj>
</cffunction>

<cffunction name="getClasses" access="public" output="false" returntype="struct" hint="returns map of classes that can be created.">
<cfreturn variables.classes>
</cffunction>

<cffunction name="onMissingMethod" access="public" output="false" returntype="any" hint="provides virtual api [new{transientName}] for any registered transient.">
<cfargument name="MissingMethodName" type="string" required="true" />
<cfargument name="MissingMethodArguments" type="struct" required="true" />
<cfif (len(arguments.MissingMethodName) gt 3) and (Left(arguments.MissingMethodName,3) is "new")>
<cfreturn create(Right(arguments.MissingMethodName,Len(arguments.MissingMethodName)-3),arguments.MissingMethodArguments)>
</cfif>
</cffunction>

<!--- private --->

<cffunction name="getClassPath" access="private" output="false" returntype="string" hint="returns the transient class path.">
<cfargument name="transientName" type="string" required="true">
<cfreturn variables.classes[arguments.transientName]>
</cffunction>

<!--- dependencies --->

<cffunction name="setBeanInjector" access="public" returntype="void" output="false">
<cfargument name="BeanInjector" type="any" required="true">
<cfset variables.BeanInjector = arguments.BeanInjector >
</cffunction>

<cffunction name="getBeanInjector" access="public" returntype="any" output="false">
<cfreturn variables.BeanInjector />
</cffunction>

</cfcomponent>
A big thanks to Bob Silverberg for co-authoring this component. You can grab a copy attached as an enclosure.

13 responses so far ↓

Jaime Metcher - Nov 9, 2008 at 5:22 PM

Paul,

How does this compare to using ColdSpring to instantiate transients?

Jaime

Paul Marcotte - Nov 10, 2008 at 2:52 AM

Hi Jaime,

Brian Kotek wrote a great explanation about using ColdSpring for transients. I could paraphrase, but I'll just post this from the quickstart that came out with release 1.2.

http://www.coldspringframework.org/coldspring/examples/quickstart/index.cfm?page=singletons

Jaime Metcher - Nov 10, 2008 at 9:51 PM

Good explanation from Brian. I was just after your take on whether and what extent your factory component avoids those caveats WRT transients. For example, is it indeed faster than ColdSpring?

Luis Majano - Nov 11, 2008 at 6:02 PM

Great stuff Paul!!

This functionality is built in to the new coming ColdBox 2.6.2 in the beanFactory plugin. It will autowire all your dependencies for you, cache if needed and even do after DI Complete methods. I'll post more on it soon.

But it not only creates transients, but you can declare cache metadata on the objects themselves and create singletons or time expired objects.

Henry Ho - Nov 24, 2008 at 3:06 PM

What do we need a Transient Factory?

Why don't we let the Service layer handle object creation?

e.g.: ABCservice.createABC() that returns CreateObject("component", ABC")

Henry Ho - Nov 24, 2008 at 3:07 PM

edit: WHY do we need a Transient Factory?, not 'what'.

Paul Marcotte - Nov 24, 2008 at 3:24 PM

Hi Henry,

I chose to build a generic transient factory to encapsulate the object creation in a single place. It allows me to configure the component paths rather than editing all my services should that path change.

In addition, if that transient has a dependency, the TransientFactory with autowire it for me.

Lastly, if I need to run a post-initialization and autowiring setup method, I can define that as well.

All this allows me to keep my service methods lean.

Matt Williams - Dec 11, 2008 at 10:48 AM

Paul, I'm considering the use of your TransientFactory. I like what I see here. One thing that wasn't obvious to me until actually testing it is that the "classes" constructor-arg is a struct of the names and paths to the transient objects you may wish to create. At first I was under the impression you could use this to create objects dynamically (I guess by passing in the class path). And it took me a minute to realize your "ServiceReturn" and "Timer" entries were examples and not necessary to run the Factory itself. I now understand that you register any potential class paths in the ColdSpring xml. Works for me, I just thought this note may help others coming along and trying this out.

Paul Marcotte - Dec 16, 2008 at 8:13 PM

Hi Matt,

Thanks for pointing that out. You're right, the ColdSpring example above is provided to demonstrate how one initializes the Transient Factory with the class paths to your transient objects. One could use a MapFactoryBean as well...

prashant - Jan 15, 2009 at 3:14 AM

hi,

i understood most of the code, unable to understand, from where bininjector has been injected and what's in bin injector, bin injector is something in Coldspring or its part of this code sample, if yes, where is the code for bin injector in this example.

Paul Marcotte - Jan 17, 2009 at 6:31 PM

Hi prashant,

You can grab the beaninjector and many other useful utilities at http://coldspringutils.riaforge.org as referenced in the post.

Joshua Curtiss - Apr 13, 2009 at 3:47 PM

This is a great idea. Thanks. I'm using this.

Redskins Jersey - Sep 21, 2011 at 4:20 AM

Fabregas free running, running, and he used his tactical awareness of a great help for us. Coach Guardiola said. Among the melon handsome imagination, Washington Redskins Jerseys Albert Haynesworth Jersey Brian Orakpo Jersey Chris Cooley Jersey Chris Horton Jersey Fabregas will play more events on the pitch middle midfielder role. During the Arsenal, Fabregas repeated muscle injuries. But in fact, Washington Redskins Jersey Clinton Portis Jersey Darrell Green Jersey DeAngelo Hall Jersey Devin Thomas Jersey he and Lionel Messi, as has a very strong muscles, so to adapt to the Barcelona game no problem. This makes Guardiola was very satisfied. His goal should be to go beyond the resume Harvey. Redskins Jersey Dexter Manley Jersey Mike Sellers Jersey Sammy Baugh Jersey Tim Hightower Jersey Guardiola said. Back to Barcelona, abregas is happy.

Leave a Comment

Leave this field empty: