Web DevCenter    
 Published on Web DevCenter (http://www.oreillynet.com/javascript/)
 See this if you're having trouble printing code examples


Programming ColdFusion MX, 2nd Edition

Top Ten Tips for Developing ColdFusion Components

by Rob Brooks-Bilson, author of Programming ColdFusion, 2nd Edition
09/24/2003

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.

1. Create an init() Method

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.

Related Reading

Programming ColdFusion MX
Creating Dynamic Web Applications
By Rob Brooks-Bilson

2. Make the Most of Auto-Generated Documentation

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.

3. Introspect a CFC Programmatically

Component metadata can also be introspected programmatically using the getMetaData() function. 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#">

4. Get in the Habit of Using Optional Attributes

The tags that are used to write CFCs--cfcomponent, cfproperty, cffunction, and 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 cfcomponent, cfproperty, cffunction, and 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 access to 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 returntype="void".

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.

5. Use Shared Variable Scopes to Improve Performance

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.

6. Watch Optional Arguments

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 a name argument, ColdFusion throws an error.

7. Suppress Whitespace

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 output that 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 cffunction container.

In terms of whitespace generation, unless your constructor area/method will be generating output, always set output="no" to avoid generating unnecessary whitespace.

8. Get Organized

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:

com.example

The actual directory structure could be:

c:\webroot\com\example

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.

9. Be Careful Not to Overexpose Your Data

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/Unnamed Scope

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.

Local Scope

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

The this scope is functionally equivalent to the this scope in JavaScript and ActionScript. Variables in 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 prefix: this.varName. Outside of the CFC, you use the component name to reference variables in the this scope: 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.

10. Be Resourceful

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:

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.


Return to the Web DevCenter.

Copyright © 2009 O'Reilly Media, Inc.