| By Brendan O'Hara | Article Rating: |
|
| April 3, 2003 12:00 AM EST | Reads: |
14,281 |
Last month I introduced design patterns, including the Template Method pattern and how it encourages polymorphism and helps remove the common switch-case constructs we normally utilize in custom tags for purposes of code reuse. This month our topic is the Iterator pattern, a simple yet powerful design pattern you can use to generically traverse through a custom collection CFC. You'll need to know what a collection is and exactly where the Iterator pattern comes into the picture. I'll go over Java Iterator syntax and demonstrate a ColdFusion implementation using CFCs. First, it's important to understand what a collection is and what it is useful for.
CFCs as Custom Collections
In most object-oriented languages a collection is simply an object that groups multiple other objects within a single container. In the Web world, collections are often useful for retrieving data from a database table or XML file, storing data somewhere in memory, allowing the data to be manipulated, and then updating the original table or file. Collections encourage software reuse by providing a standard interface for grouping "objects" and can provide algorithms to manipulate them. Java has a large number of collection classes, including Vector, HashMap, and ArrayList among others. Programmers can inherit from these classes or use them as data members when creating custom collection classes. ColdFusion has only a few collection data types (array, query, list, and struct) and until recently did not have a way to write custom collection classes. With the introduction of CFCs that's now possible.
I may want to write a generic CFC that encapsulates database access for the Employee table at my company. I will call it EmployeeCollection.cfc (see Listing 1). Initially, I give it a single method, Init(), that runs the basic query and loads the collection. I can add additional methods like Add() and Remove() to manipulate the data in the collection and therefore the Employee table. My collection will also have a single accessible data member, which is a query result set containing rows from the Employee table.
What Exactly Is an Iterator, Anyway?
An iterator, which is sometimes known as a cursor or enumeration, is an extremely powerful interface for accessing items from a collection one item at a time. The word "iterate" means "repeat or do many times." An "iterator" is therefore "something that repeats or does the same thing many times." Basically, it is a looping construct where you don't need to know the data structure in order to loop over it. The collection may store its internal data as a list, array, or query. It also takes the responsibility for traversal through collections of data away from the collections themselves and puts it into a standard iterator interface. Because it is separate from the collection itself, it also allows more than one iterator to exist at the same time. Additional iterators can be implemented that filter collections against certain criteria. An example of this may be an iterator implementation that only displays news articles based on defined preferences or one that filters corporate documents based on security level.
There are two general types of iterators: internal and external. In an internal iterator the iterator itself determines how the iteration takes place before applying some additional processing. The more common external iterator gives control of iteration to the programmer through a few standard methods.
Iterators and Enumerations Explained
Unlike some design patterns, the Iterator pattern has numerous implementations and variations in different languages. Those of you familiar with Java may know two similar but different types of interfaces (iterator and enumeration) used for navigating and traversing collections. These will give us a general idea of how we may implement iterators in ColdFusion.
In Java, the Enumeration interface was originally the preferred traversal method with just two methods:
The Java enumeration interface has now been succeeded as the preferred method of collection traversal by the iterator interface. The Java iterator interface now contains the following methods, two of which are nearly identical to HasMoreElements() and NextElement():
Here we've only added the optional ability to remove items from a collection during traversal and marginally shortened the syntax. Let's take a look at a possible ColdFusion implementation using a base class named AbstractIterator.cfc and three derived classes - QueryIterator.cfc, ArrayIterator.cfc, and ListIterator.cfc. These will allow polymorphic iteration through lists, arrays, and query result sets.
A ColdFusion Iterator
AbstractIterator.cfc (see Listing 2) is a simple abstract CFC whose derived CFCs iterate various ColdFusion collections. The AbstractIterator.cfc contains the following methods:
Just as in Java, this CFC's HasNext() method lets you test if there is at least one more element in the collection. This allows you to use a rarely used "conditional loop" with the following syntax, in which "It" is the iterator CFC:
<cfloop condition="#It.hasNext()#">
#It.next()#
</cfloop>
This syntax would be virtually identical whether you were iterating through an array, list, or query. Most ColdFusion collection data types contain objects that are all of the same data type (usually strings), but some can contain a multiplicity of data types. Let's look at some ordered ColdFusion data types, which are really collections. We'll see how we would normally traverse their internal data and how we would traverse their data using an iterator.
List
A list is a collection of string values (which can be numeric as well) separated by one or more delimiters.
<cfloop list=" Cat,Dog,Bull,Moose index="animal">
<cfoutput># animal #</cfoutput>
</cfloop>
If you have a collection that stores information in a list it should return a ListIterator.cfc (see Listing 3). In the following example the variable "It" is a ListIterator for a list containing "Cat", "Dog", "Bull", and "Moose". Here is how you would access the list's data:
<cfloop condition="#It.hasNext()#">
<cfoutput>#It.next()#</cfoutput>
</cfloop>
Array
A ColdFusion array is a numerically indexed collection of objects, which can also be of multiple data types. If you wanted to output the words "Cat", "Dog", "Bull", and "Moose", which are stored in myArray, you would do this:
<cfloop from="1" to="#ArrayLen(myArray)#" index="X">
<cfoutput>#myArray[X]# </cfoutput>
</cfloop>
If you have a collection that stores information in an array it should return an ArrayIterator.cfc (see Listing 4). In the following example the variable "It" is an ArrayIterator for an array containing "Cat", "Dog", "Bull", and "Moose". Here is how you would access the array's data:
<cfloop condition="#It.hasNext()#">
<cfoutput>#It.next()#</cfoutput>
</cfloop>
Notice that this is exactly the same syntax we used for the List iterator. This is because using a polymorphic interface like iterator allows us to standardize syntax. This allows you to write custom tags, functions, and CFCs that take an iterator CFC as a parameter and do some processing on every item in the collection, or perhaps only items that fit some specific criteria. But what about the most common ColdFusion collection data type, the query result set?
Iterating Through a Result Set
In your earliest days with ColdFusion you learned how to call a query with a simple select statement, and navigate through and output the results of the query using CFOUTPUT or CFLOOP. It was simple, and although everything was coded inline, it worked - we've been using and manipulating query result sets ever since. If you have a query result set named "qEmployees", which contains the fields "firstname" and "lastname" for every employee in your company, you would do this:
<cfoutput query="qEmployees">
#FirstName# #LastName#<br>
</cfoutput>
We may have graduated at some point to using <cfloop> so as not to nest one set of <cfoutput> tags within another set. If you have a collection that returns a QueryIterator.cfc (see Listing 5) named "It", which contains the fields "firstname" and "Lastname" for every employee in your company, you would do this:
<cfloop condition="#It.hasNext()#">
<cfset employee = It.next()>
# employee.firstName# # employee.lastName#<br>
</cfloop>
The only difference between this and the ListIterator.cfc and ArrayIterator.cfc examples is that the QueryIterator.cfc returns a structure, which must be set into a variable and then used explicitly. However, the ArrayIterator.cfc could just as easily return a structure since arrays can contain any data type.
Now that we have an idea of what an iterator interface is going to look like and what it should do, we need to look at EmployeeCollection.cfc and add the ability to access its employee data via an iterator.
Adding an Iterator() to a Collection CFC
When we last looked at EmployeeCollection.cfc it had only a single method Init(), which simply acted as a constructor to query the Employee table and build the EmployeeCollection CFC. Now we have to add an Iterator() method that returns a QueryIterator.cfc for accessing the collection's internal data. To traverse any collection using this syntax a CFC, which extends AbstractIterator, is returned by the Iterator() method.
The way I implement this interface across projects is that I require CFC collection objects, which store data such as arrays, result sets, and lists, to implement a single public method Iterator(), which returns an iterator for that collection. As I said above, filtered iterators can be optionally implemented allowing iteration over a subset of the collection or using a specific algorithm. You can "reset" an iterator after it's exhausted its collection, or at any time, if you add an optional Reset() method. You can also create a new and distinct iterator instance for each usage by simply calling the Collections Iterator() method again and assigning it to a different variable. Please note that although query result sets are passed by reference in ColdFusion MX, QueryIterator.cfc duplicates the result set so each iterator object is a wholly separate entity. Let's take a look at the Iterator() method we will add to EmployeeCollection CFC (see Listing 6).
<cffunction name="Iterator"access="public"
returntype="com.myCompany.AbstractIterator"
<cfset var myIterator = createObject
("component","com.myCompany.
QueryIterator")>
<cfinvoke component="#myIterator#"
method="init"
collection="#my.resultset#">
<cfreturn myIterator>
</cffunction>
First, we instantiate a QueryIterator.cfc using the createObject() method. Then we call the QueryIterator.cfc's own Init() method by passing in the local CFC variable my.resultset (which contains the EmployeeCollection.cfc result set). We then return the QueryIterator.cfc reference. Iteration happens exactly as our previous QueryIterator.cfc example.
Create Your Own Iterator Implementations
The Iterator pattern and associated methods are very flexible and can be molded to match your performance and programmatic needs. In this article I've discussed what an iterator is and why they are useful. Try wrapping your database access in CFC collections and objects and create an iterator that fits your individual needs. Dissect the AbstractIterator.cfc, QueryIterator.cfc, ArrayIterator.cfc, and ListIterator.cfc to see how all this is being accomplished programmatically - you'll find it quite simple. The examples from these files and IteratorExamples.cfm (see Listing 7) will get you started.
Published April 3, 2003 Reads 14,281
Copyright © 2003 SYS-CON Media, Inc. — All Rights Reserved.
Syndicated stories and blog feeds, all rights reserved by the author.
More Stories By Brendan O'Hara
Brendan O'Hara is a software architect and CEO of Exos Technology LLC, a software consulting firm in the Philadelphia suburbs.
He co-authored the Advanced Macromedia ColdFusion MX Application Development, published by Macromedia Press, and was
technical reviewer for Programming ColdFusion MX by O'Reilly. Brendan is a Team Macromedia volunteer for
ColdFusion and chairman and founder of the Philadelphia Developers Network. bohara@exostechnology.com
- Adobe’s Aiming ColdFusion at Multiple Clouds
- Cloud Computing Journal: Adobe to Deliver ColdFusion in the Cloud
- Adobe Reader Sued
- Adobe May Cooperate with Apple to Transplant Flash Player to iPhone
- Adobe Flex Developer Earns $100K in New York City
- Adobe LiveCycle Enterprise Suite 2 for Cloud Computing
- Adobe Cans Another 9% of its Workforce
- Adobe Betas Target RIAs and Cloud Computing
- Adobe MAX 2009 Online
- Thinking of Flex in London
- Moyea DVD4Web Converter V2.0 Converts DVD to FLV Fast and Synchronously with Watermarks
- Adobe & Salesforce Cut Cloud Deal
- Adobe’s Aiming ColdFusion at Multiple Clouds
- Eval JavaScript in a Global Context
- Fig Leaf Software to Exhibit at Government IT Conference & Expo
- Is Microsoft as Free as Open Source?
- Cloud Computing Journal: Adobe to Deliver ColdFusion in the Cloud
- Adobe Reader Sued
- The Planet Named “Bronze Sponsor” of Cloud Computing Expo
- Microsoft Expression Web Has Got Game
- Adobe May Cooperate with Apple to Transplant Flash Player to iPhone
- Bruce Chizen Joins Voyager Capital as Venture Partner
- My Top Seven Wishes From Adobe MAX 2009
- Adobe Flex Developer Earns $100K in New York City
- The Next Programming Models, RIAs and Composite Applications
- Where Are RIA Technologies Headed in 2008?
- Constructing an Application with Flash Forms from the Ground Up
- AJAX World RIA Conference & Expo Kicks Off in New York City
- CFEclipse: The Developer's IDE, Eclipse For ColdFusion
- Personal Branding Checklist
- Adobe Flex 2: Advanced DataGrid
- Has the Technology Bounceback Begun?
- Building a Zip Code Proximity Search with ColdFusion
- i-Technology Viewpoint: We Need Not More Frameworks, But Better Programmers
- The Asynchronous CFML Gateway
- Web Services Using ColdFusion and Apache CXF



































