PHP
  Home arrow PHP arrow Page 3 - The Iterator Pattern
Dev Shed Forums  
Administration  
AJAX  
Apache  
BrainDump  
DHTML  
Flash  
Java  
JavaScript  
Multimedia  
MySQL  
Oracle  
Perl  
PHP  
Practices  
Python  
Reviews  
Security  
Smartphone Development  
Style-Sheets  
Web Services  
XML  
Zend  
Zope  
Mobile Linux  
App Generation ROI  
IBM® developerWorks  
Forums Sitemap  
E-Commerce Hosting  
Linux Web Hosting  
Managed Hosting  
Small Business Hosting  
VPS Hosting  
Weekly Newsletter

 
Developer Updates  
Free Website Content 
 RSS  Articles
 RSS  Forums
 RSS  All Feeds
Write For Us Get Paid  
Request Media Kit
Contact Us  
Site Map  
Privacy Policy  
Support  
 USERNAME
 
 PASSWORD
 
 
  >>> SIGN UP!  
  Lost Password? 
Google.com  
PHP

The Iterator Pattern
By: php|architect
  • Search For More Articles!
  • Disclaimer
  • Author Terms
  • Rating: starstarstarstarstar / 10
    2005-11-23


    Table of Contents:
  • The Iterator Pattern
  • Extending Lendable
  • Sample Code
  • A Variant Iterator API

  • Rate this Article: Poor Best 
      ADD THIS ARTICLE TO:
      error-file:tidyout.log Del.ici.ous error-file:tidyout.log Digg
      error-file:tidyout.log Blink error-file:tidyout.log Simpy
      error-file:tidyout.log Google error-file:tidyout.log Spurl
      error-file:tidyout.log Y! MyWeb error-file:tidyout.log Furl
    Email Me Similar Content When Posted
    Add Developer Shed Article Feed To Your Site
    Email Article To Friend
    Print Version Of Article
    PDF Version Of Article

     
     
    ADVERTISEMENT


    The Iterator Pattern - Sample Code
    ( Page 3 of 4 )

    The first step in implementing the GoF Iterator pattern within Library is to write a new test case for the new concrete Iterator. Since each test method will manipulate a Library filled with Media instances, you can employ the UnitTestCase::setUp() method to populate a variable with a Library in a known state for each test.

    Start by adding the Library::getIterator() method as a Factory for instances of the LibraryGofIterator class.

      class IteratorTestCase extends UnitTestCase {
       
    protected $lib;
       
    function setup() {
         
    $this->lib = new Library;
         
    $this->lib->add(new Media('name1', 2000));
         
    $this->lib->add(new Media('name2', 2002));
         
    $this->lib->add(new Media('name3', 2001));
       
    }
       
    function TestGetGofIterator() {
         
    $this->assertIsA($it = $this->lib->getIterator()
            ,'LibraryGofIterator');
       
    }
      }

    Here's the implementation:

      class Library {
       
    // ...
       
    function getIterator() {
         
    return new LibraryGofIterator($this->collection);
       
    }
     
    }

    The getIterator() method passes the Library's $collection to the constructor of the new concrete iterator. This technique has two important implications: each iterator is independent, so multiple iterators can operate at the same time. Additionally, the iterator operates on the collection as it existed at the time the iterator was requested. If another item is added to the collection at any time later, you must request another iterator to display it (at least in this implementation).

    Let's continue enhancing the test suite by adding assertions to the TestGetGofIterator() method to match the Iterator design pattern. The isDone() method should only be true if you've iterated over the entire collection. If the iterator's just been created, isDone() should obviously return false to indicate it's okay to iterate.

      class IteratorTestCase extends UnitTestCase {
       
    function setup() { /* ... */ }
       
    function TestGetGofIterator() {
         
    $this->assertIsA($it = $this->lib->getIterator()
            ,'LibraryGofIterator');
         
    $this->assertFalse($it->isdone());
       
    }
     
    }

    As usual with TDD, implement the simplest possible code that satisfies your test case:

      class LibraryGofIterator {
       
    function isDone() {
         
    return false;
       
    }
     
    }

    So, what should happen during the first iteration?currentItem() should return the first Media object added in the IteratorTestCase::setUp() method and isDone() should continue to be false, since two additional items remain to be iterated over.

      class IteratorTestCase extends UnitTestCase {
       
    function setup() { /* ... */ }
       
    function TestGetGofIterator() {
         
    $this->assertIsA($it = $this->lib->getIterator()
            ,'LibraryGofIterator');
         
    $this->assertFalse($it->isdone());
         
    $this->assertIsA($first = $it->currentItem(), 'Media');
         
    $this->assertEqual('name1', $first->name);
         
    $this->assertFalse($it->isdone());
       
    }
     
    }

    It's critical that LibraryGofIterator receives the $collection in the constructor (see the minimal implementation of Library above) and returns the current() item of that array from the currentItem()method.

      class LibraryGofIterator {
       
    protected $collection;
       
    function __construct($collection) {
         
    $this->collection = $collection;
       
    }
       
    function currentItem() {
         
    return current($this->collection);
       
    }
       
    function isDone() {
         
    return false;
       
    }
     
    }

    What should happen in the next iteration? The next()method should change what item is returned by the currentItem() method. This next test captures that expected behavior:

      class IteratorTestCase extends UnitTestCase {
       
    function setup() { /* ... */ }
       
    function TestGetGofIterator() {
         
    $this->assertIsA($it = $this->lib->getIterator(), 'LibraryGofIterator');
         
    $this->assertFalse($it->isdone());
         
    $this->assertIsA($first = $it->currentItem(), 'Media');
         
    $this->assertEqual('name1', $first->name);
         
    $this->assertFalse($it->isdone());
         
    $this->assertTrue($it->next());
         
    $this->assertIsA($second = $it->currentItem(), 'Media');
         
    $this->assertEqual('name2', $second->name);
         
    $this->assertFalse($it->isdone());
       
    }
     
    }

    Piggybacking again on PHP's array functions, use next() on the array:

      class LibraryGofIterator {
       
    protected $collection;
       
    function __construct($collection) {
         
    $this->collection = $collection;
       
    }
       
    function currentItem() {
         
    return current($this->collection);
       
    }
       
    function next() {
         
    return next($this->collection);
       
    }
       
    function isDone() {
         
    return false;
       
    }
     
    }

    The third iteration looks much like the others, except the isDone() method must return true. You also want next() to indicate success of moving to the next iteration:

      class IteratorTestCase extends UnitTestCase {
        
    function setup() { /* ... */ }
       
    function TestGetGofIterator() {
          
    $this->assertIsA($it = $this->lib->getIterator(), 'LibraryGofIterator');
         
    $this->assertFalse($it->isdone());
          
    $this->assertIsA($first = $it->currentItem(), 'Media');
         
    $this->assertEqual('name1', $first->name);
         
    $this->assertFalse($it->isdone());
          
    $this->assertTrue($it->next());
         
    $this->assertIsA($second = $it->currentItem(), 'Media');
         
    $this->assertEqual('name2', $second->name);
         
    $this->assertFalse($it->isdone());
         
    $this->assertTrue($it->next());
         
    $this->assertIsA($third = $it->currentItem(), 'Media');
         
    $this->assertEqual('name3', $third->name);
         
    $this->assertFalse($it->next());
         
    $this->assertTrue($it->isdone());
       
    }
     
    }

    With small modifications to the next() and isDone()methods, all of the tests pass Here's the code so far:

      class LibraryGofIterator {
       
    protected $collection;
       
    function __construct($collection) {
         
    $this->collection = $collection;
        
    }
       
    function first() {
          
    reset($this->collection);
       
    }
       
    function next() {
         
    return (false !== next($this->collection));
       
    }
       
    function isDone() {
         
    return (false === current($this->collection));
       
    }
       
    function currentItem() {
         
    return current($this->collection);
       
    }
     
    }

    There's just one problem with the Iterator test case: it doesn't reflect how iterators are typically used. Yes, it tests all of the features of the Iterator pattern, but application code uses the Iterator in a much simpler way. So, the next step is to write a test using more realistic code.

      class IteratorTestCase extends UnitTestCase {
       
    protected $lib;
       
    function setup() { /* ... */ }
       
    function TestGetGofIterator() { /* ... */ }
        function TestGofIteratorUsage() {
         
    $output = '';
         
    for ($it=$this->lib->getIterator(); !$it->isDone(); $it->next()){
           
    $output .= $it->currentItem()->name;
         
    }
         
    $this->assertEqual('name1name2name3', $output);
       
    }
     
    }

    So far, the implementation of Iterator copies an array (the collection) and uses PHP's internal pointer to track the iteration. You can also implement the Iterator by keeping track of the collection index by yourself. This requires a new accessor method in Library to fetch an object by key.

      class Library {
       
    // ...
       
    function get($key) {
         
    if (array_key_exists($key, $this->collection)) {
           
    return $this->collection[$key];
         
    }
       
    }
     
    }

    Also, you'd pass $this (the library itself) to the constructor instead of $this->collection (the array containing the Media collection) in the Library::getIterator() method.

    The "external" iterator would then just track a pointer internally to know which element of the Library collection it's currently referencing, and would use the reference to the Library passed in the constructor to call the get() method to retrieve the current object.

      class LibraryGofExternalIterator {
       
    protected $key = 0;
       
    protected $collection;
       
    function __construct($collection) {
         
    $this->collection = $collection;
       
    }
       
    function first() {
         
    $this->key=0;
       
    }
       
    function next() {
         
    return (++$this->key < $this->collection->count());
       
    }
       
    function isDone() {
         
    return ($this->key >= $this->collection->count());
       
    }
       
    function currentItem() {
         
    return $this->collection->get($this->key);
       
    }
     
    }

    This implementation assumes your collection array is indexed starting with 0 and is completely sequential.



     
     
    >>> More PHP Articles          >>> More By php|architect
     

       

    PHP ARTICLES

    - Adding Ordering and Grouping Clauses to the ...
    - Implementing Factory Methods in PHP 5
    - Merging a File Split for FTP Upload using PHP
    - Getting Data from Yahoo Site Explorer Inboun...
    - Method Chaining: Adding More Selecting Metho...
    - How to Split a File During an FTP Upload Usi...
    - Expanding a Custom CodeIgniter Library with ...
    - Using the Yahoo Site Explorer Inbound Links ...
    - Building a CodeIgniter Custom Library with M...
    - Building an E-mini Trading System Using PHP ...
    - Completing the MySQL Class with Method Chain...
    - Building Dynamic Queries with Chainable Meth...
    - PHP Encryption and Decryption Methods
    - Building a MySQL Abstraction Class with Meth...
    - Completing a Sample String Processor with Me...





    © 2003-2009 by Developer Shed. All rights reserved. DS Cluster 4 Hosted by Hostway
    For more Enterprise Application Development news, visit eWeek