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 fondamentaliLa 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.
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 valoreabstract public mixed current(void)
: restituisce il valore correnteabstract public scalar key(void)
: restituisce la chiave correnteabstract public void next(void)
: sposta il puntatore all’elemento successivoabstract public boolean valid(void)
: restituiscetrue
se l’iteratore è ancora valido (non ha raggiunto la fine della collezione), altrimentifalse
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/>"; ?>
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/>"; ?>
AppendIterator
), filtrare i valori indesiderati (FilterIterator
), scorrere indefinitamente (InfiniteIterator
), iterare un sottoinsieme di elementi (LimitIterator
) o iterare in modo ricorsivo (RecursiveIteratorIterator
).- 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.
Ancora nessun commento