Iterator è uno dei più noti modelli di progettazione grazie al suo utilizzo in diversi linguaggi di programmazione come Python, Java, C++ e PHP. Fondamentalmente ciò che questo modello propone è di trasferire la responsabilità di attraversare una collezione a una nuova classe, che dovrebbe utilizzare una interfaccia standard definita in una classe astratta o a un’interfaccia, in modo che si possa percorrere simile alle tuple del database, ai nodi in un documento XML, agli elementi di un array, o qualsiasi altro tipo di collezione che ci viene in mente.

Concetti fondamentali
L’idea chiave del pattern Iterator consiste nel trasferire la responsabilità dell’accesso e della navigazione attraverso gli elementi a una classe separata dal contenitore: l’iteratore.
La classe iteratore consente di visitare ad uno ad uno l’insieme degli elementi di un contenitore come se fossero ordinati in una sequenza finita. A questo scopo, un oggetto iteratore deve tenere traccia dell’ultimo elemento visitato e deve essere in grado di calcolare qual è l’elemento successivo nella sequenza di attraversamento.
Per permettere di visitare qualunque aggregato di elementi di un tipo concreto, indipendentemente dall’iteratore specifico e dal contenitore ad esso associato, il pattern Iterator richiede la definizione di due interfacce:

  • contenitore (Aggregate),
  • iteratore (Iterator).

La prima interfaccia è condivisa da tutte le classi contenitore e definisce un’operazione per ottenere un nuovo iteratore della classe stessa; la seconda definisce l’insieme di operazioni comuni ad ogni tipo di iteratore.

Lo Standard PHP Library SPL fornisce un set standard di interfacce per PHP5. Lo scopo di SPL è quello di implementare alcune interfacce di accesso ai dati e classi per PHP. Funzionalmente è stato progettato per attraversare strutture di aggregati (tutto quello che si può ciclare). Questi possono includere array, set di risultati database, strutture ad albero XML, elenchi di directory o ad altri elenchi a tutti. Attualmente SPL si occupa di iteratori.
Utilizzo di Iterator
Per creare il nostro iteratore in PHP dobbiamo/possiamo implementare l’interfaccia Iterator. Questo ci permette di attraversare la nostra collezione, usando un foreach, per esempio. L’interfaccia Iterator dichiara i seguenti metodi:

  • abstract public void rewind(void): sposta il puntatore al primo elemento, e non restituisce alcun valore
  • abstract public mixed current(void): restituisce il valore corrente
  • abstract public scalar key(void): restituisce la chiave corrente
  • abstract public void next(void): sposta il puntatore all’elemento successivo
  • abstract public boolean valid(void): restituisce true se l’iteratore è ancora valido (non ha raggiunto la fine della collezione), altrimenti false

Come esempio di iteratore in PHP, implementeremo qualcosa di simile alla classe DatePeriod di PHP 5.3, che permette di scorrere i giorni in un intervallo di date.

<?php
class Intervallo {
    public $inizio;
    public $fine;

    function __construct($inizio, $fine) {
        $this->inizio = strtotime($inizio);
        $this->fine = strtotime($fine);
    }
}
?> 
<?php
class IteratorePerGiorno implements Iterator {
	private $intervallo;

	function __construct(Intervallo $intervallo) {
		$this->intervallo = $intervallo;
	}

    function rewind() {
        $this->chiaveCorrente = 1;
        $this->dataCorrente = $this->intervallo->inizio;
    }

    function key() {
        return $this->chiaveCorrente;
    }

    function current() {
        return date('Y-m-d', $this->dataCorrente);
    }

    function next() {
        $this->chiaveCorrente++;
        $this->dataCorrente = strtotime('+1 day', $this->dataCorrente);
    }

    function valid() {
        return ($this->dataCorrente <= $this->intervallo->fine);
    }
}
?>  
<?php
require_once 'Intervallo.php';
require_once 'IteratorePerGiorno.php';

$intervallo = new Intervallo('2012-06-12', '2013-06-06');
$iteratore = new IteratorePerGiorno($intervallo);
foreach($iteratore as $num => $data)
	echo "Il giorno " . $num . " dell'intervallo è " . $data . "<br/>";
?> 
Utilizzo di IteratorAggregate
Se vogliamo seguire il modello Iterator alla lettera (raccomandato), in modo che l’aggregato crei sé stesso l’iteratore e lo restituisca, la nostra collezione dovrà implementare l’interfaccia IteratorAggregate e il suo metodo abstract public Traversable getIterator(void). Se facciamo questo, possiamo delegare la collezione al foreach direttamente: il nostro codice client sarà più semplice e chiaro.
<?php
require_once 'IteratorePerGiorno.php';

class Intervallo implements IteratorAggregate {
    public $inizio; 
    public $fine;

    function __construct($inicio, $fin) {
        $this->inizio = strtotime($inizio);
        $this->fine = strtotime($fine);
    }

    public function getIterator() {
        return new IteratorePerGiorno($this);
    }
}
?> 
<?php
require_once 'Intervallo.php';

$intervallo = new Intervallo('2012-06-12', '2013-06-06');
foreach($intervallo as $num => $data)
	echo "Il giorno " . $num . " dell'intervallo è " . $data . "<br/>";
?> 
La libreria standard di PHP (SPL) ha anche diversi iteratori predefiniti che possono essere interessanti, oltre a una serie di classi che consentono di concatenare più iteratori (AppendIterator), filtrare i valori indesiderati (FilterIterator), scorrere indefinitamente (InfiniteIterator), iterare un sottoinsieme di elementi (LimitIterator) o iterare in modo ricorsivo (RecursiveIteratorIterator).
Confronto con gli indici
Nei linguaggi procedurali è comune usare indici interi per scorrere gli elementi di un array. Sebbene con alcuni contenitori orientati agli oggetti possano essere usati anche indici interi, l’uso degli iteratori ha i seguenti vantaggi:

  • I cicli di conteggio non sono adatti per tutte le strutture dati; in particolare, per le strutture dati in cui l’accesso diretto è lento o assente, come le liste e gli alberi.
  • Gli iteratori possono fornire un modo coerente di iterare sulle strutture dati di ogni categoria, e perciò rendono il codice più leggibile, riusabile, e meno sensibile ai cambiamenti nella struttura dati.
  • Un iteratore può imporre restrizioni di accesso aggiuntive, come assicurare non si saltino degli elementi o che non si visiti più volte lo stesso elemento.
  • Un iteratore può consentire all’oggetto contenitore di essere modificato senza invalidare l’iteratore. Per esempio, dopo che un iteratore è avanzato oltre il primo elemento può essere possibile inserire elementi aggiuntivi all’inizio del contenitore con risultati predicibili. Con l’uso di indici, questo è problematico dal momento che gli indici devono cambiare.
Similari
Overloading di metodi in Java
14% Java
Un metodo overload viene utilizzato per riutilizzare il nome di un metodo ma con argomenti diversi, opzionalmente con un differente tipo di ritorno. [expand title=”Regole per overload” startwrap=”” endwrap=”” excerpt=”⤽” s…
Elencare file e directory con PHP
13% Php
Questo articolo illustra un usuale compito che si potrebbe avere sperimentato durante lo sviluppo di un’applicazione in PHP: elenchi di file e directory. Si occupa di diverse soluzioni di base e avanzate, ciascuno con i su…
Sporca e veloce finestra modal
9% JQuery
Se avete bisogno di una finestra modale e avete jQuery caricato si può semplicemente personalizzare e incollare le seguenti righe di codice in qualsiasi gestore di eventi o eventhandler per far apparire rapidamente un mess…
Metodi magici e costanti predefinite in PHP
8% Php
PHP fornisce un insieme di costanti predefinite e metodi magici per i nostri programmi. A differenza delle normali costanti i quali si impostano con define(), il valore delle costanti predefinite o speciali dipendono da do…
Interazione con i campi di un form con CSS
7% Css
Grazie al supporto da parte dei maggiori browser dei CSS Selectors versione 2.1, alcuni semplici effetti grafici possono essere proposti tramite fogli di stile in maniera leggera. In questo post vedremo come evidenziare il…