Iterator jest operacyjnym wzorcem projektowym, który zapewnia sekwencyjny dostęp do elementów zbioru bez ujawniania jego reprezentacji. W PHP istnieje mechanizm iteratorów, jednakże, żeby lepiej zrozumieć na czym on polega postanowiłem zaimplementować Iterator.

Na poniższym obrazku znajduje się struktura wzorca:

Wzorzec Iterator w PHP

Wzorzec Iterator w PHP

Na podstawie wzorca utworzyłem interfejsy i klasy potrzebne do jego implementacji oraz przykładową klasę Book, która będzie reprezentowała książkę.

Interfejs Iteratora

interface IIterator
{
   function next();
   function isDone();
   function currentItem();
   function hasNext();
}

Metoda next zwróci następny element w zbiorze.

Metoda isDone sprawdzi czy Iterator zakończył swoje działanie

Metoda currentItem zwróci aktualny element iteratora

Metoda hasNext sprawdzi czy istnieje następny element w Iteratorze.

Interfejs IAggregate

interface IAggregate
{
   function createIterator();
}

Posiada metodę służącą do stworzenia i zwrócenia iteratora.

Aby ułatwić sobie pisanie  i nie powtarzać go stworzyłem klasę abstrakcyjną Collection, która zawiera implementacje przydatnych metod w zbiorze, takich jak dodaj element, usuń element itp.

abstract class Collection
{
   protected $items;
   
   protected function __construct()
   {
      $this->items = array();
   }
   
   public function addItem($item)
   {
      array_push($this->items, $item);
   }
   
   public function removeItem($position)
   {
      if(isset($this->items[$position]))
      	 unset($this->items[$position]);
   }
}

 

Dość już tej abstrakcji, tym razem stworzyłem klasę Book reprezentującą ksiązkę

class Book
{
   private $name;
   
   function __construct($name)
   {
      $this->name = $name;
   }
   public function getName()
   {
      return $this->name;
   }
}

Klasa w konstruktorze ustawia tytuł książki oraz posiada metodę getName(), która ten tytuł zwróci.

Następnie stworzyłem BookIterator czyli klasę reprezentującą Iterator dla zbioru książek. W lepszej wersji metody wykorzystane w tej klasie powinny być dziedziczone z abstrakcyjnej klasy Iterator, by mogły zostać wykorzystane w innych iteratorach, ale nie chciałem już bardziej mieszać w kodzie.

class BookIterator implements IIterator
{
   private $position;
   private $items;
   
   function __construct($items)
   {
      $this->items 	= $items;
      $this->position 	= -1;
   }
   
   public function next()
   {
   	return $this->items[++$this->position];
   }
   
   public function hasNext()
   {
        return isset($this->items[$this->position + 1]);
   }
   
   public function isDone()
   {
    	return count($this->items) == $this->position - 1;
   }
   
   public function currentItem()
   {
   	$this->items[$this->position];
   }
}

Klasa BookIterator implementuje interfejs IIterator i definiuje metody, które ten interfejs zawiera. Są to proste operacje na zbiorach, tablicach, które jeśli to czytasz i dotarłeś do tego momentu, raczej nie trzeba Ci tłumaczyć.

Ostatnią klasą potrzebną do działania kodu będzie klasa BooksCollection, reprezentująca zbiór książek.

class BookCollection extends Collection implements IAggregate
{
        function __construct()
	{ 
	    parent::__construct();
	} 
	public function createIterator()
	{
		return new BookIterator($this->items);
	}
}

Klasa ta dziedziczy z klasy Collection oraz implementuje interfejs IAggregate. W klasie tej wywoływany jest chroniony konstruktor z klasy Collection oraz zadeklarowana jest metoda, która służy do stworzenia Iteratora.

Mając to wszystko sprawdźmy jak to działa:

//utworzenie 3 przykładowych ksiązek

$bookA = new Book('Lord of rings');
$bookB = new Book('50 shades of gray');
$bookC = new Book('Hunger games');

//utworznie zbioru ksiazek i dodanie wczesniej utworzonych obiektów do zbioru

$books = new BookCollection();
$books->addItem($bookA);
$books->addItem($bookB);
$books->addItem($bookC);

//utworzenie ksiązkowego iteratora
$iterator = $books->createIterator();

//jesli iterator posiada nastepny element to go pobierz i wyświetl

while($iterator->hasNext())
{
   $book = $iterator->next();
   echo $book->getName()."\n";
}

Efektem działania kodu będzie wypisanie tytułów 3 książek w zbiorze.

Kiedy używać Iteratora?

  • Jeśli chcesz udostępnić jednolity interfejs do poruszania się po zbiorach, kolekcjach w swoim programie, aplikacji.
  • Jeśli chcesz uzyskać dostęp do zagregowanego obiektu i jego elementów bez ujawnienia jego reprezentacji

Iterator w PHP

W PHP istnieje zdefiniowany interfejs do iteratora i agregatora. Dzięki nim możesz używać iteratora np w pętli foreach.

Więcej na:

http://pl1.php.net/manual/en/class.iterator.php

http://pl1.php.net/manual/en/class.iteratoraggregate.php

http://en.wikipedia.org/wiki/Iterator_pattern