This article, the first of two parts, explains how to use the Iteratorpattern to manipulate any collection of objects. It is excerpted fromchapter eight of the book PHP|architect's Guide to PHP Design Patterns, written by Jason E. Sweat (PHP|architect, 2005; ISBN: 0973589825).
Lendable is a good, generic start. Let's extend it to track items like DVDs or CDs.
Media extends Lendable and tracks details about specific media, including the name of the item, the year it was released, and what type of item it is:
class Media extends Lendable { public $name; public $type; public $year; public function _construct($name, $year, $type='dvd') { $this->name = $name; $this->type = $type; $this->year = (int)$year; } }
To keep things simple, Media has three public instance variables, Media::name, Media::year, and Media::type. The constructor takes two arguments and stores the first in $name and the second in $year. The constructor also allows an optional third parameter to specify type (which defaults to "dvd").
Given individual objects to manipulate, you can now create a container to hold them: a Library. Like a regular library, Library should be able to add, remove and count the items in the collection. Eventually, Library should also permit access to individual items (objects) in the collection (which is shown momentarily in the Sample Code section of this chapter).
For right now, let's build a test case for Library.
class LibraryTestCase extends UnitTestCase { function TestCount() { $lib = new Library; $this->assertEqual(0, $lib->count()); } }
It's easy enough to write a class that satisfies this test:
class Library { function count() { return 0; } }
Let's continue and add some interesting features to the test:
class LibraryTestCase extends UnitTestCase { function TestCount() { /* ... */ } function TestAdd() { $lib = new Library; $lib->add('one'); $this->assertEqual(1, $lib->count()); } }
An easy way to implement add() is to piggyback on PHP's flexible array functions: you can add items to an array instance variable and use count() to return the number of items in the collection.
class Library { protected $collection = array(); function count() { return count($this->collection); } function add($item) { $this->collection[] = $item; } }
Library is now a collection, but it provides no way to retrieve or manipulate the individual members of the collection.
Let's move on to the purpose of the chapter, implementation of the Iterator design pattern.
The following UML class diagram shows the GoF Iterator pattern with the Media and Library classes used to make the example concrete.
Your collection class must provide a Factory (see Chapter 3) to create an instance of your Iterator.
Iterator classes define an interface of first() to go to the beginning of a collection, next() to move to the next item in sequence as you iterate, currentItem() to retrieve the current item from the collection as you iterate, and isDone() to indicate when you have iterated over the entire collection.
In the Sample Code section, the LibraryGofIterator class is an example of a direct implementation of the GoF Iterator design pattern.