Mi chiedevo che cosa rende speciale Iterator rispetto ad altri costrutti simili, e che ha reso la Banda dei Quattro elencarlo come un modello di progettazione.
L'iteratore si basa sul polimorfismo (una gerarchia di raccolte con un'interfaccia comune) e sulla separazione delle preoccupazioni (l'iterazione sulle raccolte dovrebbe essere indipendente dal modo in cui i dati sono strutturati).
E se sostituiamo la gerarchia delle raccolte con, ad esempio, una gerarchia di oggetti matematici (intero, float, complesso, matrice ecc.) E l'iteratore da una classe che rappresenta alcune operazioni correlate su questi oggetti, ad esempio funzioni di potenza. Il diagramma di classe sarebbe lo stesso.
Probabilmente potremmo trovare molti altri esempi simili come Writer, Painter, Encoder e probabilmente altri migliori, che funzionano allo stesso modo. Tuttavia, non ho mai sentito nessuno di questi essere chiamato Design Pattern.
Cosa rende speciale Iterator?
È forse più complicato perché richiede uno stato mutabile per memorizzare la posizione corrente all'interno della raccolta? Ma poi, lo stato mutevole di solito non viene considerato desiderabile.
Per chiarire il mio punto, lasciami fare un esempio più dettagliato.
Ecco il nostro problema di progettazione:
Diciamo che abbiamo una gerarchia di classi e un'operazione definita sugli oggetti di queste classi. L'interfaccia di questa operazione è la stessa per ogni classe, ma le implementazioni possono essere completamente diverse. Si presume anche che abbia senso applicare l'operazione più volte sullo stesso oggetto, ad esempio con parametri diversi.
Ecco una soluzione ragionevole per il nostro problema di progettazione (praticamente una generalizzazione del modello iteratore):
Per separare le preoccupazioni, le implementazioni dell'operazione non devono essere aggiunte come funzioni alla gerarchia di classi originale (oggetti operando). Dato che vogliamo applicare l'operazione più volte sullo stesso operando, dovrebbe essere rappresentato da un oggetto che contiene un riferimento all'operando, non solo da una funzione. Pertanto l'oggetto operando dovrebbe fornire una funzione che restituisce l'oggetto che rappresenta l'operazione. Questo oggetto fornisce una funzione che esegue l'operazione effettiva.
Un esempio:
C'è una classe base o un'interfaccia MathObject
(nome stupido, lo so, forse qualcuno ha un'idea migliore.) Con classi derivate MyInteger
e MyMatrix
. Per ciascuna dovrebbe essere definita MathObject
un'operazione Power
che consenta il calcolo di quadrati, cubi e così via. Quindi potremmo scrivere (in Java):
MathObject i = new MyInteger(5);
Power powerOfFive = i.getPower();
MyInteger square = powerOfFive.calculate(2); // should return 25
MyInteger cube = powerOfFive.calculate(3); // should return 125