Top Ten Tips for Developing ColdFusion Componentsby Rob Brooks-Bilson, author of Programming ColdFusion, 2nd Edition
ColdFusion Components, or CFCs, as they are more commonly referred to, are a new construct introduced in ColdFusion MX and greatly enhanced in version 6.1. They allow you to move from totally procedural development to a more object-oriented approach. Because this represents a new paradigm for many ColdFusion developers, and the existing literature on CFCs is still in the early stages, I've assembled a list of tips to keep in mind when developing with CFCs.
I hesitate to call these tips best practices, as something better always seems to come along, but I don't hesitate to recommend them as ways to enhance your CFC development practices.
Since ColdFusion MX doesn't support constructors the way many object-oriented languages (such as Java) do, one technique that's being adopted by ColdFusion developers is to use an "init" method to act as a default
constructor, initializing a CFC instance. Developers can use the
init() method to initialize constants to be used by the component, as well as to return an instance of the component back to the calling page. A simple
init() method in a CFC might look something like this:
<cfcomponent displayname="shoppingCart"> <cffunction name="init" access="public" output="no" returntype="shoppingCart"> <cfargument name="cartID" type="UUID" required="yes"> <!--- Putting the variable in the variables scope makes it available to the init() method as well as all other methods in the CFC and to the "pseudo-constructor area ---> <cfset variables.cartID = arguments.cartID> <cfreturn this> </cffunction> ... </cfcomponent>
This init method takes a single argument,
cartID. It then assigns the value of
arguments.cartID to a variable in the variables scope called
cartID. Variables in the
variables scope are available anywhere in the CFC, meaning any method in the CFC can use the variable.
To call that
init() method from a .cfm page (typically your Application.cfm page), you can use a technique called method chaining:
<cfset myCart = createObject("component","shoppingCart").init(cartID=createUUID())>
Alternatively, you can also use the
cfinvoke tag to do the same thing:
<cfinvoke component="shoppingCart" method="init" returnvariable="myCart" cartID="#createUUID()#" />
If you want to avoid having to manually call the
init() method upon instantiation of the CFC, you could make the call within the "constructor" area of the CFC. The constructor area automatically runs whenever you instantiate a CFC:
<cfcomponent displayname="shoppingCart"> <!--- call init() automatically when the CFC is instantiated ---> <cfset init()> <cffunction name="init" access="public" output="no" returntype="shoppingCart"> <cfargument name="cartID" type="UUID" required="no" default="#createUUID()#"> <!--- Putting the variable in the variables scope makes it available to the init() method as well as all other methods in the CFC and to the "pseudo-constructor area ---> <cfset variables.cartID = arguments.cartID> <cfreturn this> </cffuntion> ... </cfcomponent>
Regardless of which technique you use, the end result is an efficient way to create an instance of a CFC while setting initialization parameters that can be used throughout the CFC.
Well-documented code is something all developers aspire to, but often fail to deliver on. The engineers at Macromedia have made documenting CFCs as painless as possible by making CFCs self-documenting. Even if you put no effort whatsoever into commenting your CFC code, ColdFusion MX, as well as both Dreamweaver MX and Flash MX, are able to automatically generate basic documentation for each CFC. For developers willing to invest a little time up front (see the next tip), this auto-generated documentation can be quite rich.
All of the various tag attributes used by the component tags, along with any user-defined attributes, are available as component metadata. This metadata exists as a ColdFusion structure that can be used for all sorts of purposes. ColdFusion MX comes with a tool called the CFC Explorer that you can use to introspect any CFC on your ColdFusion server. To use the CFC Explorer, simply fire up a browser and enter the URL to the CFC you want to introspect. You'll need to enter the RDS password for the server to use the CFC browser. The CFC Explorer displays a nicely formatted screen containing the component's metadata, including the component's name, path on the server, properties, and methods.
If this isn't a viable option (if you are in a shared hosting environment, for example), you may want to look into a tool called the CFCRemoteDocumenter, by Nathan Dintenfass. The CFCRemoteDocumenter is a CFML file you can include in your CFCs. It contains several methods for generating a nicely formatted page containing everything you ever wanted to know about a CFC. In fact, you might find it even more useful than the CFC Explorer that comes with ColdFusion MX. You can download a copy of the CFCRemoteDocumenter, along with documentation and examples, from Nathan's web site.
You can use another tool included with ColdFusion MX called the Component Browser to list all packages and CFCs hosted on your server. To launch the Component Browser, open your web browser and point it to localhost/cfide/componentutils/componentdoc.cfm.
As with the CFC Explorer, you'll be presented with a login screen; you'll need to enter the RDS password for your ColdFusion MX server before being able to proceed.
Component metadata can also be introspected programmatically using the
When you use
getMetaData() within a CFML page, it returns a structure containing metadata such as
properties, methods, and parameters for the specified CFC. When used within a CFC, pass the
this scope to
GetMetaData() and it returns all of the metadata for the component.
Here's a piece of code that displays the metadata for the CFC Explorer component, which ships with ColdFusion MX:
<cfscript> myObj = createObject("component", "cfide.componentutils.cfcexplorer"); metadata = getMetaData(myObj); </cfscript> <h2>Metadata for the CFC Explorer</h2> <cfdump var="#metadata#">
The tags that are used to write CFCs--
cfargument--each have optional attributes that you don't have to include if you don't want to. Although it's often tempting to leave them off and just accept the default values, you may want to think twice about doing so.
Suffice it to say, the more attributes you specify in your
cfargument tags, the better the auto-generated CFC documentation will be, and the easier it will be to tell what a specific component does. Besides the standard attributes, you can pass any additional arbitrary attributes to the tag as user-defined attributes. Although these attributes don't do anything, they are available as metadata when the component is introspected using the
getMetaData() function. You
may use any attribute names you wish, with the exception of those reserved by the various CFC tags.
Many of the optional attributes, especially for the
cffunction tag, can have a big impact on the behavior of the component method. For example, the access attribute of the
cffunction tag determines what code can access the method. The default is
public, meaning any .cfm template or CFC on the server can
access the method. If you want to expose the method as a web service, you'll need to change
remote. Another important optional attribute of the
cffunction tag is
returntype. This attribute specifies the data type of any value returned by the method. If you omit this attribute, it defaults to
any. If you specify a value for this attribute, however, you can be assured that the method will only return the type of value you specify. Further, if you design your method to not return any value at all, you can set
These are just some examples of the optional attributes available to the various tags that are used in CFCs. The bottom line is that getting in the habit of investing a little extra time specifying optional attributes now will save you a lot more time in the long run.
If your application makes extensive use of CFCs, it makes sense that you would want to avoid the overhead of repeatedly instantiating the various CFCs as a user navigates through the application. Likewise, if you build an e-commerce type of application that uses a shopping cart, you would most likely want an instance of the cart CFC for each user. In both of these cases, it makes sense to store instances of your CFCs in a shared scope. In ColdFusion MX, it's possible to assign CFC instances to the server, the application, and the session scopes. Here's an example of how you can instantiate a CFC, in this case a shopping cart, and assign it to the session scope (place this code in your Application.cfm file.):
<cfif not isDefined('session.shoppingCart')> <cfset session.shoppingCart = createObject("component","shoppingCart").init(cartID=createUUID())> </cfif>
And this is the
init() method within your shoppingCart.cfc:
<cfcomponent displayname="shoppingCart"> <cffunction name="init" access="public" output="no" returntype="shoppingCart"> <cfargument name="cartID" type="UUID" required="yes"> <!--- Putting the variable in the variables scope makes it available to the init() method as well as all other methods in the CFC and to the "pseudo-constructor area ---> <cfset variables.cartID = arguments.cartID> <cfreturn this> </cffuntion> ... </cfcomponent>
If you look at the
cfreturn tag, you'll notice that we return
this. This is special in that what we returned is an instance of the component.
If you plan to expose your CFCs as web services, be careful with optional arguments. The Apache Axis 1.1 web services engine used by ColdFusion MX 6.1 does not support optional input arguments. If your CFC methods contain optional arguments, the required attribute setting is ignored, and the argument is treated as required. Here's an example:
<cfcomponent> <cffunction name="getMessage" access="remote" returntype="string" output="no"> <cfargument name="name" type="string" required="no" default="Stinky"> <cfreturn "Hello " & arguments.name &"! " & "I've been invoked as a web service."> </cffuntion> </cfcomponent>
In this case, even though
name is an optional argument as far as the CFC is concerned, it is required when you expose it as a web service. If you try to call the component method as a web service and don't provide
name argument, ColdFusion throws an error.
If you are concerned about unnecessary whitespace in the output generated by ColdFusion (and you should
be), there is a simple step you can take to reduce the amount generated by your CFCs. Both the
cffunction tag and the
cfcomponent tag have an optional Boolean attribute called
determines how both output and whitespace are handled:
<cfcomponent output="no"> <cffuncfion name="foo" output="no"> ... </cffuntion> </cfcomponent>
In the case of the
cfcomponent tag, the
output attribute indicates whether the component's "pseudo-constructor" code (any area within the
cfcomponent container that is outside of a
cffunction container) allows output. If
yes, expressions (surrounded with pound (
#) signs) are automatically evaluated without the need for
cfoutput tags within component methods, and the method is permitted to display output. If
no, ColdFusion suppresses any output within the constructor. If output is not specified, ColdFusion allows output within the constructor, but all expressions must be within
cfoutput tags. For the
cffunction tag, the output attribute works the same way, except that it applies only to the area within the
In terms of whitespace generation, unless your constructor area/method will be generating output, always
output="no" to avoid generating unnecessary whitespace.
Where you store your CFCs can have an impact on how efficiently and effectively you write your applications. Although it's possible to just save your CFCs in the same directory as the .cfm pages that call them, you would be missing out on a big benefit to using CFCs: code reuse. By storing all of your CFCs in a central location, it's possible for multiple applications (or multiple parts of an application) to share them. One of the easiest ways to do this is to save your CFCs in packages. In the Java world, packages are used to organize class files into related groups. In ColdFusion MX, packages work in the same basic way. Packages map directly to a directory structure on your server. They follow a customary naming convention that reverses your domain name.
So, if your company's domain is "example.com," you would create a package hierarchy for your CFCs that looks something like this:
The actual directory structure could be:
If you don't want your CFCs accessible to web browsers, move the hierarchy out of your web root, and create a mapping to the com directory in the ColdFusion Administrator. This way, you can still refer to your CFC hierarchy within your code as if it were located in your web root, without having to potentially expose your CFCs to curious browsers.
Within the package hierarchy, you can group your CFC by application or functionality:
com.example.storefront com.example.storefront.ui com.example.blog com.example.util
When you instantiate a component that's stored in a package, you refer to it using dot notation:
<cfset session.myCart = createObject("component", "com.example.storefront.shoppingcart")>
In this case, you are instantiating shoppingcart.cfc, located in the com.example.storefront package. Note that you don't specify the .cfc extension on the component you are calling.
When working with variables inside of your CFCs, make sure you aren't exposing your data any more than you intend to. Depending on how you scope (or don't scope) your variables, you may make a variable readable/writable to another CFC method, or even code outside of your CFC. Here's a list of the three main scopes you need to concern yourself with when writing CFCs, along with an explanation of where they can be accessed.
Variables in the variables scope are available only within the CFC. They are available to the constructor code, and to all component methods and any pages they include. They may be prefixed with the variables prefix or left unscoped (often called the unnamed scope). Variables in the variables scope that are set within the calling page are not automatically available within the component's variables scope. Additionally, variables in the variables scope exist as long as the component object exists. This allows them to persist between method calls.
This differs from variables you set in the "local" scope of a method by using the
var keyword. These variables are available only to the method in which they are defined and any pages included by that method. Additionally, local variables do not last beyond the life of the method call. The local variable
scope differs from other variable scopes in that there is no scope prefix used for the variable. Note that
if you do not define the variable with the
var keyword, the variable exists in the variables scope, not the local scope.
this scope is functionally equivalent to the
this scope are available to all methods within a CFC, any pages they include, and the page that calls the CFC. Within a CFC, you refer to variables in the
this scope using the
this.varName. Outside of the CFC, you use the component name to reference variables in the
myComponent.varName. Note that this applies only when you instantiate the CFC as an object via the
cfobject tag or the
createObject() function; not when a component method is invoked using the
cfinvoke tag, without instantiating the CFC separately.
Although CFCs are still relatively new, there are a lot of good resources for learning more about them. Your first stop should be the ColdFusion MX 6.1 LiveDocs. After that, you may want to check out these additional resources:
CFCZone.org: Clearinghouse for all things CFC. Contains open source CFCs, a blog, CFC FAQ, and a listserv dedicated to CFC discussion.
Mach-II: A development framework built using CFCs, which has the potential to do for ColdFusion developers what Struts has done for Java developers.
Macromedia ColdFusion MX Developer Center: Macromedia's official ColdFusion developer center, with several articles and tutorials related to CFCs.
Corfield.org: Contains a blog, as well as other information on various CFC-related topics, from Macromedia's Director of Architecture.
cfoop: A site dedicated to object-oriented programming in ColdFusion MX.
Fullasagoog: A blog aggregator that contains more than 25 ColdFusion MX blogs, many with regular CFC-related postings.
Rob Brooks-Bilson is a freelance writer and a senior technology manager at Amkor Technology. He is the author of O'Reilly's Programming ColdFusion MX, 2nd Edition (covering CF MX 6.1).
O'Reilly & Associates recently released (August 2003) Programming ColdFusion, 2nd Edition.
Chapter 11: "Advanced Database Techniques," is available free online.
For more information, or to order the book, click here.
Return to the Web DevCenter.
Copyright © 2009 O'Reilly Media, Inc.