The Iterator Pattern (
Page 1 of 4 )
This article, the first of two parts, explains how to use the Iterator
pattern to manipulate any collection of objects. It is excerpted from
chapter eight of the book PHP|architect's Guide to PHP Design Patterns, written by Jason E. Sweat (PHP|architect, 2005; ISBN: 0973589825).
OBJECT-ORIENTED
PROGRAMMING ENCAPSULATES application logic in classes. Classes, in
turn, are instantiated as objects, and each individual object has a
distinct identity and state. Individual objects are a useful way to
organize your code, but often you want to work with a group of objects, or a collection. A set of rows from a SQL query is a collection, as is the list of Property objects in the Monopoly game examples shown earlier in the book.
A collection need not be homogeneous either. A Window object in a graphical user interface framework could collect any number of control objects—a Menu, a Slider, and a Button,
among others. Moreover, the implementation of a collection can vary: a
PHP array is a collection, but so is a hash table, a linked list, a
stack, and a queue.
The Problem
How can one easily manipulate any collection of objects?
The Solution
Use the Iterator pattern to provide uniform access to the contents of a collection.
You may not realize it, but you use the Iterator pattern every day—it's embodied in PHP's array type
and rich set of array manipulation functions. (Indeed, given the
combination of the native array type in the language and a host of
flexible functions designed to work with this native type, you need a
pretty compelling reason not to use arrays as your means of
manipulating collections of objects.)
Here's native array iteration in PHP:
$test = array(‘one’, ‘two’, ‘three’);
$output = '';
reset($test);
do {
$output .= current($test);
} while (next($test));
echo $output; // produces 'onetwothree'
The reset() function restarts iteration to the beginning of the array; current() returns the value of the current element; and next() advances to the next element in the array and returns the new current()value. When you advance past the end of the array, next() returns false. Using these iteration methods, the internal implementation of a PHP array is irrelevant to you.
Iterator couples the object-oriented programming principles of encapsulation and polymorphism. Using Iterator,
you can manipulate the objects in a collection without explicitly
knowing how the collection is implemented or what the collection
contains (what kinds of objects). Iterator provides a uniform
interface to different concrete iteration implementations, which do
contain the details of how to manipulate a specific collection,
including which items to show (filtering) and in what order (sorting).
Let's create a simple object to manipulate in a collection. (Though this example is in PHP5, Iterators are
not unique to PHP5 and most of the examples in this chapter work in
PHP4 as well, albeit with a healthy amount of reference operators
added). The object, Lendable, represents
media such as movies and albums and is intended to be part of a web
site or service to let users review or lend portions of their media
collection to other users. (For this example, do not concern yourself
with persistence and the like.)
Let's start with the following test as a basis for the design of Lendable.
// PHP5
class LendableTestCase extends UnitTestCase {
function TestCheckout() {
$item = new Lendable;
$this->assertFalse($item->borrower);
$item->checkout('John');
$this->assertEqual('borrowed', $item->status);
$this->assertEqual('John', $item->borrower);
}
function TestCheckin() {
$item = new Lendable;
$item->checkout('John');
$item->checkin();
$this->assertEqual('library', $item->status);
$this->assertFalse($item->borrower);
}
}
To implement the requirements of this initial test, let's create a
class with a few public attributes and some methods to toggle the
values of these attributes:
class Lendable {
public $status = 'library';
public $borrower = '';
public function checkout($borrower) {
$this->status = 'borrowed';
$this->borrower = $borrower;
}
public function checkin() {
$this->status = 'library';
$this->borrower = '';
}
}