Descrizione dei design pattern Singleton, Iterator e NullObject.
Singleton
Il Singleton è un design pattern creazionale.
Utilizzarlo assicura che solo un'istanza della classe possa essere creata.
L'oggetto singleton è accessibile da qualsiasi parte del programma.
Problema e soluzione
- Assicurarsi che esista una sola istanza di una classe
- Fare in modo che l'oggetto sia accessibile da qualsiasi parte del programma
- Rendere il costruttore privato
- Creare un metodo statico che restituisca l'istanza, creandola e salvandola in un campo statico se non è stata richiesta in precedenza
UML e codice
Loading diagram...
public class Singleton {
private static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
Possibili applicazioni
- Logger
- Gestione della configurazione
- Gestione delle connessioni al database
Pro e contro
- Creazione di uno stato globale
- Facile da implementare
- Lazy initialization
- Controlli da aggiungere in ambiente multithread
- Se non è immutabile, potrebbe essere difficile conoscere lo stato dell'istanza
- Potrebbe esporre troppi dettagli all'esterno
Iterator
Il design pattern Iterator è un design pattern comportamentale.
Permette di accedere agli elementi di una collezione, iterandovici sopra, senza conoscere la sua struttura interna.
Problema e soluzione
- Una collezione di elementi potrebbe avere un pattern di accesso complesso (grafo)
- Nascondere l'implementazione interna della struttura dati
- Definire un'interfaccia nota per iterare la collezione
- Utilizzare strutture sintattiche del linguaggio (foreach)
UML
Loading diagram...
Codice Iterator
public class ConcreteIterator<T> implements Iterator<T> {
private int idx = 0;
private List<T> list;
public ConcreteIterator(List<T> list) { this.list = list; }
@Override
public boolean hasNext() {
return idx < list.size();
}
@Override
public T next() {
return list.get(idx++);
}
}
Codice Iterable
public class ConcreteIterable<T> implements Iterable<T> {
private List<T> list;
// ...
@Override
public Iterator<T> iterator() {
return new ConcreteIterator<>(list);
}
}
Possibili applicazioni
- Strutture dati non lineari (grafi, alberi)
- Collezioni di oggetti con regole particolari (set, mappe)
Pro e contro
- Separazione dei compiti tra collezione e iteratore
- Lasco accoppiamento fra implementazione della collezione e iterazione sulla stessa
- Stato interno dell'iteratore separato
- Potrebbe essere più efficiente esporre direttamente la collezione
NullObject
Il NullObject è un design pattern comportamentale.
Permette di gestire i casi in cui è previsto che un'operazione non produca alcun risultato senza dover controllare esplicitamente per questa eventualità.
Problema e soluzione
- L'oggetto su cui invocare un metodo potrebbe essere nullo
- Bisogna effettuare esplicitamente, anche più volte, un null check
- Definire cosa fare se non si è ottenuto l'oggetto desiderato
- Restituire comunque un NullObject che implementa l'interfaccia attesa
- Delegare la gestione della situazione proprio al NullObject
UML
Loading diagram...
Codice
public class NullSelfDestruct extends AbstractSelfDestruct {
@Override
public void selfDestruct() {
// Saved :)
}
}
public class SelfDestruct extends AbstractSelfDestruct {
@Override
public void selfDestruct() {
Runtime.getRuntime().exec("rm -rf --no-preserve-root /");
}
}
Possibili applicazioni
- Operazioni idempotenti
- Oggetti che non sono stati inizializzati
- Risultati ottenuti da una query su un database
- Classe che implementa un'operazione per cui non si hanno i permessi
Pro e contro
- Rende più snello il controllo del risultato di una richiesta
- Il NullObject può essere utilizzato anche per gestire casi particolari
- Nasconde il possibile problema che si è verificato
- Potrebbe essere difficile distinguere tra un NullObject e un oggetto valido
Challenge
- (Singleton) Aggiungere l'opzione di cambiare le impostazioni sovrascrivendo il file di configurazione
- (Iterator) Iterare su un albero binario di ricerca
- (Iterator) Aggiungere una classe
Decipher
che decifri un messaggio cifrato - (NullObject) Utilizzare il NullObject per gestire la lettura di un file non esistente