 |
 |
Ben Forta Senior
Product
Evangelist for
ColdFusion |
| |
|
|
|
 |
|
Code reuse is good. It's such a good thing, in fact, that Macromedia
ColdFusion lets you reuse code in several ways, from simple includes
to custom tags and user-defined functions. Over the past few years,
ColdFusion developers have come to understand what these methods
are and the better developers out there made a point of taking
advantage of code reuse so they could build better applications.
And then, just when everything was clear and well understood,
things got a little more complicated.
ColdFusion components (CFCs) are the new application building
blocks introduced in Macromedia ColdFusion MX. They do the seemingly
impossible—marrying the power of objects with the simplicity
of CFML.
|
ColdFusion components: not your old custom tags
CFCs allow, and even encourage, the creation of structured applications.
Of all the code reuse options, CFCs are most like custom tags.
However, there are some very important differences between them:
- Custom tags have a single entry point; CFCs can have multiple
entry points. This makes it possible to create a single component
that does many related actions. (To do that with custom tags
you'd need multiple tags or cumbersome switch processing.)
- Custom tags have no formalized parameter passing and validation
mechanism; CFCs do. In other words, unlike custom tags, CFCs
can validate passed data, enforce data types, check for required
parameters, and optionally assign default values.
- Custom tags cannot persist; CFCs can. Custom tags are blocks
of code that are executed as is, while CFCs are objects and
can be treated as such.
- Custom tags are designed to contain code; CFCs are designed
to contain both code and data.
- Custom tags are accessible only by ColdFusion and only locally;
CFCs can be accessed as web services, opening up a whole new
world of reuse possibilities.
|
| |
| To sum it up, CFCs and
custom tags are quite different. Although their functionality does
overlap a little, they really do not solve the same problems at
all. Let's look at an example. The code sample below, user.cfc,
abstracts and encapsulates user processing. It has three methods:
- List obtains a list of users returning a query containing
first and last names and user IDs.
- Get returns a user record retrieved from two tables.
- GeEMail retrieves a user's e-mail address.
|
| Here's the code for user.cfc: |
<!--- User component --->
<CFCOMPONENT HINT="User processing">
<!--- List users method --->
<CFFUNCTION NAME="List"
RETURNTYPE="query"
HINT="Get complete user list">
<!--- Get users --->
<CFQUERY NAME="users" DATASOURCE="exampleapps">
SELECT EmployeeID AS UserID, FirstName, LastName
FROM tblEmployees
ORDER BY LastName, FirstName
</CFQUERY>
<CFRETURN users>
</CFFUNCTION>
<!--- Get user method --->
<CFFUNCTION NAME="Get"
RETURNTYPE="query"
HINT="Get a complete user record">
<CFARGUMENT NAME="UserID"
TYPE="string"
REQUIRED="true"
HINT="Employee ID is required">
<!--- Get user email --->
<CFQUERY NAME="user" DATASOURCE="exampleapps">
SELECT EmployeeID AS UserID, FirstName,
LastName, Title, Email,
Phone, DepartmentName
FROM tblEmployees, tblDepartments
WHERE tblEmployees.DeptIDFK=tblDepartments.DepartmentID
AND EmployeeID='#ARGUMENTS.UserID#'
</CFQUERY>
<CFRETURN user>
</CFFUNCTION>
<!--- Get user method --->
<CFFUNCTION NAME="GetEMail" RETURNTYPE="string">
<CFARGUMENT NAME="UserID"
TYPE="string"
REQUIRED="true"
HINT="Employee ID is required">
<!--- Get user record --->
<CFQUERY NAME="user" DATASOURCE="exampleapps">
SELECT Email
FROM tblEmployees
WHERE EmployeeID='#ARGUMENTS.UserID#'
</CFQUERY>
<CFRETURN user.Email>
</CFFUNCTION>
</CFCOMPONENT> |
The beauty of user.cfc is that it removes all database interaction
from an application. Instead of having to use a <CFQUERY>
to obtain a user list, you could do the following: |
<!--- Get user list --->
<CFINVOKE COMPONENT="user"
METHOD="List"
RETURNVARIABLE="ListRet"> |
The value returned
by this <CFINVOKE> call
is a query and can be used as such.
Here's another example, this time displaying a user's e-mail
address:
|
<!--- Load user object --->
<CFOBJECT COMPONENT="user"
NAME="usrObj">
<!-- Display email address --->
#usrObj.GetEMail(URL.userid)# |
Here the user component
is being loaded as an object (into the default VARIABLES
scope, as no other scope was specified), which is then used to execute
a particular method. |
Building tiered applications
Now that you have seen what can be done with a ColdFusion Component,
let's look at why you should do it. Most ColdFusion developers
write spaghetti-code—a single CFML file that often contains
database queries, HTML output, forms, JavaScript, business logic
(in the form of CFML statements), and more. This type of code
is not only not reusable, it's difficult to maintain and doesn't
scale. This is why more experienced developers use custom tags
and other forms of code reuse, as I already mentioned.
Still, custom tags are not good enough. They were designed to
black-box and encapsulate functionality—essentially taking
blocks of CFML code and packaging them for reuse. Custom tags
are ideal for abstracting menuing code, encapsulating payment
processing integration, and even rendering output. Custom tags
were not designed for truly tiered application development. That's
where CFCs come into the picture.
Consider the user.cfc example from before:
- The CFC cleanly separates content from presentation, making
each far more reusable.
- The CFC makes it possible for developers working on presentation
not to have to worry about what the data is and where it comes
from. (For example, one of the methods does a JOIN to return
the data. The fact that a JOIN is needed is of no consequence
to whoever is building your UI.) The truth is, why should someone
concentrating on writing DHTML have to worry about your database
schemas? Why should they even have to know that the data is
in a database?
- The CFC allows you to make database schema changes (even replacing
the database with an LDAP server, perhaps) without having to
change a lot of code.
|
| As a result of these
qualities, developers can use CFCs in all sorts of ways. This is
the basis of tiered applications—the separation of content
from presentation—and CFCs are the essential building blocks
with which to tier applications. |
Writing for reuse
ColdFusion Components are used to create reusable code. As such,
they must be written with reuse in mind. This means that CFCs
should never make assumptions about the operating environment.
For example:
- CFCs can access all variables in all scopes, but they probably
shouldn't. If they do, then whoever uses the CFC will have to
know too much about what it does and why. In this case, if a
CFC requires a FORM field, for example, it should accept it
as an argument and not access it directly.
- CFCs should never access
SESSION
variables unless they themselves set them (for example, if all
SESSION processing was encapsulated
within a CFC). It's dangerous to assume that you know the environment
in which your CFCs will be used; after all, SESSION
variables may not exist. If you require a variable, it should
be passed to you.
- CFCs should also never access CGI variables. Sure, these variables
will always be present if the request comes from a browser,
but can you ensure that you won't need to use this component
in some other way in the future?
|
The bottom line is that
within a CFC you should never assume that you know anything about
where the request is coming from, what the request is for, what
the data will be used for, or anything else. CFCs should be capable
of standing on their own. Writing for reuse demands it. |
What about presentation?
If ColdFusion Components cannot make assumptions about their environment,
then CFCs cannot be used to render output. Because you're separating
content from presentation, CFCs should return data; they shouldn't
worry about prettying it up or generating clients-side HTML. If
your CFC is written properly it may be used by all sorts of clients,
among them:
- Web browsers (different versions with different capabilities)
- Macromedia Flash MX
- Web services (through SOAP)
- Mobile devices
|
To support multiple forms of presentation only the presentation
layer has to change; the content layer does not. Your CFCs only
process content – logic and processing that is common regardless
of presentation , and so the CFCs should not generate finished
pages. It is perfectly reasonable (and even advisable) for a CFC
to return data followed by a set of pages that present the data
in different ways. And if you need reuse at the presentation level,
custom tags are ideally suited for that task. Their syntax, especially
support for tag pairs and child tags, makes applying presentation
to returned content clean and simple.
Of course, every rule has an exception: CFCs can indeed render
content. But if you go this route, be careful not to end up right
where you started off—breaking the desired separation of
presentation and content. If you want to use CFCs to render content,
consider using inheritance in CFCs—create a base CFC that
holds all your content processing and then extend it using a series
of CFCs, one for each presentation type and each with its own
display method. This route is definitely feasible, although you
may still find that, syntactically, custom tags are better suited
for the task.
|
| |
| |
|
| About the author
Ben Forta is Macromedia's senior product evangelist and the
author of numerous books, including ColdFusion 5 Web Application
Construction Kit and its sequel, Advanced ColdFusion 5
Development. Ben is working on several new titles on ColdFusion
MX. For more information visit www.forta.com.
You can contact Ben at ben@forta.com. |
| |
| |
|
|