In questa lezione sarà illustrato il primo “design pattern” del corso.
I design pattern (letteralmente, schemi di progettazione) presentano soluzioni standard per situazioni tipiche di progettazione orientata agli oggetti.
Nell’informatica, l’uso di questo termine, e l’individuazione dei primi pattern si devono al testo “Design Patterns”, di Gamma, Helm, Johnson e Vlissides, del 1994.
Ciascun pattern è diviso in 4 parti:
In queste slide, se un pattern prevede l’uso di più classi o interfacce in relazione tra loro, presenteremo anche il diagramma UML delle classi che illustra queste relazioni.
Ricordiamo brevemente i principali elementi di un diagramma delle classi UML:
Ad esempio, nella figura a destra:
Le principali relazioni tra classi sono indicate da frecce con tratti distintivi, come illustrato in figura.
La dipendenza si può applicare ogni volta che una classe A ne utilizza in qualunque modo un’altra B:
La punta a triangolo vuoto viene usata per indicare la generalizzazione tra due classi o due interfacce (extends) e la realizzazione di un’interfaccia da parte di una classe (implements).
La relazione di aggregazione sussiste quando una classe contiene riferimenti ad un’altra.
La composizione è una forma più forte di aggregazione.
Aggregazione e composizione sono solitamente accompagnate da una cardinalità.
Per approfondire, si consulti un testo sull’UML, come “UML for Java Programmers”, di R.C. Martin.
Il primo pattern che esaminiamo prende il nome di Iterator.
Questo pattern riguarda il modo di passare in rassegna gli elementi di una data collezione o insieme di oggetti.
Si suppone, cioè, che un determinato oggetto (chiamato contenitore, o aggregato) ne contenga altri.
Il contenitore vuole permettere agli utenti (client) di passare in rassegna tutti gli oggetti contenuti, senza però esporre la sua struttura interna.
Inoltre, deve essere possibile che più client accedano concorrentemente (ovvero, in parallelo) al contenitore.
La soluzione proposta consiste essenzialmente nel creare un oggetto, chiamato iteratore, che rappresenta un indice all’interno del contenitore.
Nelle prossime slide, presentiamo il problema e la soluzione proposta, nel modo schematico tipico dei design pattern.
Contesto:
Soluzione:
La soluzione è illustrata meglio nella prossima slide, con l’ausilio di un diagramma UML.
La soluzione proposta è illustrata dal diagramma UML in figura.
L’interfaccia Iterator rappresenta l’iteratore, cioè l’indice all’interno del contenitore.
Il suo metodo next restituisce il prossimo elemento del contenitore, e contemporaneamente fa avanzare l’indice di una posizione.
Il metodo isDone restituisce vero se tutti gli elementi del contenitore sono stati visitati.
le chiamate a isDone non modificano la posizione corrente dell’indice.
L’interfaccia Aggregate rappresenta il contenitore.
Il contenitore offre un metodo createIterator che restituisce un nuovo iteratore.
Naturalmente, ci saranno classi concrete che implementeranno le due interfacce di sopra.
Tuttavia, il client potrebbe anche non conoscere tali classi concrete e limitarsi ad utilizzare le interfacce.
La figura illustra come è stato implementato il pattern ITERATOR nella libreria standard Java.
Come nel pattern, l’interfaccia Iterator rappresenta l’iteratore.
Il suo metodo next si comporta come previsto dal pattern:
Il metodo hasNext è l’opposto di isDone:
Il metodo remove, non previsto dal pattern, elimina dal contenitore l’ultimo elemento restituito da next
remove può essere chiamato una sola volta dopo una chiamata a next:
in questo caso, remove dovrebbe lanciare l’eccezione (non verificata) UnsupportedOperationException.
L’interfaccia Iterable fa le veci di Aggregate.
Il metodo iterator si comporta come createIterator, restituendo un nuovo iteratore.
Un esempio di classe concreta che implementa Iterable è LinkedList, che rappresenta una lista concatenata.
La slide successiva introduce brevemente questa classe.
Questa descrizione fa riferimento alla versione non parametrica di queste interfacce, come si trovava in Java 1.4.
A partire da Java 1.5, queste interfacce, e le classi corrispondenti, sono state aggiornate per sfruttare la programmazione parametrica.
Lezioni successive tratteranno di queste modifiche.
Le interfacce Iterable e Iterator sono contenute nel package java.util.
La classe java.util.LinkedList rappresenta una lista doppiamente concatenata.
Essa appartiene alla Java Collection Framework, che sarà oggetto di lezioni successive.
Qui, presentiamo brevemente i suoi metodi principali.
Per cominciare, LinkedList implementa Iterable, e quindi dispone di un metodo “iterator” che restituisce un iteratore.
L’esempio seguente mostra l’uso tipico di un iteratore su una lista
Mostra codiceL'output dell'esempio è la stringa "Hello world!"
Si noti il cast a String, necessario perché il tipo restituito da next è Object;
questo cast viene reso superfluo dalla versione parametrica di Iterator, che verrà presentata più avanti nel corso.
Individuare l’output del seguente programma.
Dire se la classe CrazyIterator rispetta il contratto dell’interfaccia Iterator
in caso negativo, giustificare la risposta.
Prima parte Mostra codice
Il seguente frammento di classe definisce un nodo in un albero binario.
public class BinaryTreeNode {
private BinaryTreeNode left, right;
public BinaryTreeNode getLeft() { return left; }
public BinaryTreeNode getRight() { return right; }
}
Si implementi una classe iteratore BinaryTreePreIterator che visiti i nodi dell’albero in preorder (ciascun nodo prima dei suoi figli). Tale classe deve poter essere usata nel seguente modo:
public static void main(String[] args) {
BinaryTreeNode root = ...;
Iterator i = new BinaryTreePreIterator(root);
while (i.hasNext()) {
BinaryTreeNode node = (BinaryTreeNode) i.next();
...
}
}
4. Risoluzione dell'overloading e dell'overriding
5. Controllo di uguaglianza tra oggetti
6. Classi interne, locali ed anonime
7. Iteratori, teoria e pratica
8. Clonazione di oggetti. Confronto tra oggetti.
9. Elementi di programmazione di interfacce grafiche
10. Il paradigma Model-View-Controller. Il pattern Strategy
11. I pattern Composite e Decorator
12. I pattern Template Method e Factory Method
13. Classi e metodi parametrici
14. La libreria Java Collection Framework: le interfacce Iterable, ...
15. La libreria Java Collection Framework: la classe HashSet e le l...
16. Parametri di tipo con limiti
17. L'implementazione della programmazione generica: la cancellazio...
18. La riflessione
19. Introduzione al multi-threading
22. Classi enumerate