A Coldfusion Transient Factory Example

ColdFusion , Design Patterns , ColdSpring Add comments
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 to “A Coldfusion Transient Factory Example”

  1. Jaime Metcher Says:
    Paul,

    How does this compare to using ColdSpring to instantiate transients?

    Jaime
  2. Paul Marcotte Says:
    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
  3. Jaime Metcher Says:
    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?
  4. Luis Majano Says:
    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.
  5. Henry Ho Says:
    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")
  6. Henry Ho Says:
    edit: WHY do we need a Transient Factory?, not 'what'.
  7. Paul Marcotte Says:
    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.
  8. Matt Williams Says:
    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.
  9. Paul Marcotte Says:
    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...
  10. prashant Says:
    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.
  11. Paul Marcotte Says:
    Hi prashant,

    You can grab the beaninjector and many other useful utilities at http://coldspringutils.riaforge.org as referenced in the post.
  12. Joshua Curtiss Says:
    This is a great idea. Thanks. I'm using this.
  13. UGG Says:
    <p><a href="http://www.uggbootsretails.com"; target="_blank">UGG Argyle Knit</a> features genuine twin-face sheepskin for refreshing comfort. It's one of our tallest boots and looks great all the way up or cuffed down. <a href="http://www.uggbootsretails.com"; target="_blank">UGG boots</a> in our Classic Collection feature a soft foam insole covered with genuine sheepskin and have a molded EVA light such as <a href="http://www.uggbootsretails.com"; target="_blank">UGG Sundance</a> and <a href="http://www.uggbootsretails.com"; target="_blank">UGG Bailey Button</a>, the flexible outsole designed for amazing comfort with every step. Beat the chill in superior style with the <a href="http://www.uggbootsretails.com"; target="_blank">UGG Ultra Tall</a>. The <a href="http://www.uggbootsretails.com"; target="_blank">Bailey Button 1873</a> of light and flexible outsole along with a suede heel guard provides durable wear all season long. Detailed with three oversized wood buttons, this style can be styled up and buttoned, slouched and slightly unbuttoned, or completely cuffed. <a href="http://www.uggbootsretails.com"; target="_blank">UGG Ultra Short</a> is also a good choice for people to refuge from the cold.Just buy one of the ugg boots for your family and yourself, it’s really a good gift both for you famlily and you.Action is better than thinking in heart.</p>

Leave a Reply



Powered by Mango Blog. Design and Icons by N.Design Studio