Welcome!

You will be redirected in 30 seconds or close now.

ColdFusion Authors: Yakov Fain, Jeremy Geelan, Maureen O'Gara, Nancy Y. Nee, Tad Anderson

Related Topics: ColdFusion

ColdFusion: Article

Design Patterns in ColdFusion: Iterator Pattern PART 2

Design Patterns in ColdFusion: Iterator Pattern PART 2

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:

  • HasMoreElements(): Tests if this collection contains more elements
  • NextElement(): Increments the Collections index and returns the next element

    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():

  • HasNext(): Tests if this iterator contains more elements
  • Next(): Increments the iterator and returns the next element
  • Remove(): Removes the current element from the collection (optional)

    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:

  • HasNext(): Tests if this iterator's collection contains more elements
  • Next(): Increments the Iterators index and returns the next element

    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.

  • 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. [email protected]

    Comments (0)

    Share your thoughts on this story.

    Add your comment
    You must be signed in to add a comment. Sign-in | Register

    In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.


    IoT & Smart Cities Stories
    The platform combines the strengths of Singtel's extensive, intelligent network capabilities with Microsoft's cloud expertise to create a unique solution that sets new standards for IoT applications," said Mr Diomedes Kastanis, Head of IoT at Singtel. "Our solution provides speed, transparency and flexibility, paving the way for a more pervasive use of IoT to accelerate enterprises' digitalisation efforts. AI-powered intelligent connectivity over Microsoft Azure will be the fastest connected pat...
    There are many examples of disruption in consumer space – Uber disrupting the cab industry, Airbnb disrupting the hospitality industry and so on; but have you wondered who is disrupting support and operations? AISERA helps make businesses and customers successful by offering consumer-like user experience for support and operations. We have built the world’s first AI-driven IT / HR / Cloud / Customer Support and Operations solution.
    Codete accelerates their clients growth through technological expertise and experience. Codite team works with organizations to meet the challenges that digitalization presents. Their clients include digital start-ups as well as established enterprises in the IT industry. To stay competitive in a highly innovative IT industry, strong R&D departments and bold spin-off initiatives is a must. Codete Data Science and Software Architects teams help corporate clients to stay up to date with the mod...
    At CloudEXPO Silicon Valley, June 24-26, 2019, Digital Transformation (DX) is a major focus with expanded DevOpsSUMMIT and FinTechEXPO programs within the DXWorldEXPO agenda. Successful transformation requires a laser focus on being data-driven and on using all the tools available that enable transformation if they plan to survive over the long term. A total of 88% of Fortune 500 companies from a generation ago are now out of business. Only 12% still survive. Similar percentages are found throug...
    Druva is the global leader in Cloud Data Protection and Management, delivering the industry's first data management-as-a-service solution that aggregates data from endpoints, servers and cloud applications and leverages the public cloud to offer a single pane of glass to enable data protection, governance and intelligence-dramatically increasing the availability and visibility of business critical information, while reducing the risk, cost and complexity of managing and protecting it. Druva's...
    BMC has unmatched experience in IT management, supporting 92 of the Forbes Global 100, and earning recognition as an ITSM Gartner Magic Quadrant Leader for five years running. Our solutions offer speed, agility, and efficiency to tackle business challenges in the areas of service management, automation, operations, and the mainframe.
    The Jevons Paradox suggests that when technological advances increase efficiency of a resource, it results in an overall increase in consumption. Writing on the increased use of coal as a result of technological improvements, 19th-century economist William Stanley Jevons found that these improvements led to the development of new ways to utilize coal. In his session at 19th Cloud Expo, Mark Thiele, Chief Strategy Officer for Apcera, compared the Jevons Paradox to modern-day enterprise IT, examin...
    With 10 simultaneous tracks, keynotes, general sessions and targeted breakout classes, @CloudEXPO and DXWorldEXPO are two of the most important technology events of the year. Since its launch over eight years ago, @CloudEXPO and DXWorldEXPO have presented a rock star faculty as well as showcased hundreds of sponsors and exhibitors! In this blog post, we provide 7 tips on how, as part of our world-class faculty, you can deliver one of the most popular sessions at our events. But before reading...
    DSR is a supplier of project management, consultancy services and IT solutions that increase effectiveness of a company's operations in the production sector. The company combines in-depth knowledge of international companies with expert knowledge utilising IT tools that support manufacturing and distribution processes. DSR ensures optimization and integration of internal processes which is necessary for companies to grow rapidly. The rapid growth is possible thanks, to specialized services an...
    At CloudEXPO Silicon Valley, June 24-26, 2019, Digital Transformation (DX) is a major focus with expanded DevOpsSUMMIT and FinTechEXPO programs within the DXWorldEXPO agenda. Successful transformation requires a laser focus on being data-driven and on using all the tools available that enable transformation if they plan to survive over the long term. A total of 88% of Fortune 500 companies from a generation ago are now out of business. Only 12% still survive. Similar percentages are found throug...