Continuando il discorso sui design pattern iniziato precedentemente, andiamo oggi a vedere un altro pattern molto utilizzato: il Factory Method Pattern.
Il GoF (Gang of Four Design Patterns) lo definisce così:
Definisce un’interfaccia per creare oggetti, ma lascia alle sottoclassi la
decisione del tipo di classe a istanziare.
Già dalla definizione, si può notare come il problema riguardante la parentela tra classi discusso per il Singleton non sia più presente. Infatti, si decide di ricorrere al Factory Method Pattern nel momento in cui non siamo in grado di conoscere a priori il tipo esatto dell'oggetto da creare oppure quando si vuole delegare ad altre entità il compito della creazione degli oggetti.
Consultando gran parte della letteratura, il pattern viene rappresentato con un diagramma UML simile a questo:
Elementi fondamentali del pattern
- Creator: ha il compito di dichiarare la Factory che si occuperà poi di restituire l'oggetto appropriato;
- ConcreteCreator: effettua l'override del metodo di Factory al fine di restituire l'implementazione adeguata dell'oggetto;
- Product: definisce l'interfaccia dell'oggetto che Factory deve ritornare;
- ConcreteProduct: implementa l'oggetto in base a Product;
La prima osservazione, se pur banale che va fatta è che l'implementazione è nettamente più complessa rispetto a quella quasi elementare del Singleton.
Qualche esempio
Partendo da un esempio piuttosto elementare, andiamo a definire una CatFactory:
//Product interface Cat{ public void speak (); }
Ogni elemento restituito dalla factory dovrà implementare questa interfaccia.
Andiamo ora a creare le classi concrete:
//ConcreteProduct class Abissino implements Cat{ public void speak(){ System.out.println("Abissino"); } } class AmericanCurl implements Cat{ public void speak(){ System.out.println("American Curl"); } } class Asian implements Cat{ public void speak(){ System.out.println("Asian"); } }
Ho deciso di implementare tutte le classi in maniera non pubblica, ipotizzandone il posizionamento in uno stesso file. Infatti, come sappiamo, Java vieta più di una classe pubblica in uno stesso file, in virtù del fatto che il file deve avere lo stesso nome della classe pubblica.
Il concetto fondamentale di questo pattern è proprio il fatto che ogni classe concreta sia un derivato di un tipo base. In questo caso specifico, ogni classe implementa Cat.
La classe Factory
L'elemento a cui è demandata la creazione degli oggetti è proprio la cosiddetta classe Factory, che avrà un metodo statico con al suo interno questa funzionalità.
Vediamo un'ipotetica struttura:
public class Factory{ public static Cat generateCat(String criteria){ if(criteria.equals("Abissino")) return new Abissino(); if(criteria.equals("Curl")) return new AmericanCurl(); if(criteria.equals("Asian")) return new Asian(); return null; } }
Notiamo che al momento il codice della classe Factory è relativamente semplice, perchè accetta solo tre stringhe. In un'ipotetica situazione reale, il codice sarebbe ben più lungo e complesso ma questo è sufficiente a dare un'idea su come il pattern funzioni.
Il Factory Method Pattern presenta diversi vantaggi e svantaggi, tra cui:
- rappresenta un collegamento alle sottoclassi: tramite il creator è possibile scegliere dinamicamente quale classe concreta utilizzare senza alcun impatto sull'utilizzo dell'utente finale;
- collega gerarchie di classi parallelamente: i ConcreteCreator possono collegarsi con i ConcreteProduct e generare un collegamento parallelo tra gerarchie diverse;
Perchè utilizzare il Factory Method Pattern?
Come detto in precedenza, ci sono diversi casi in cui non possiamo conoscere a priori quale sarà il tipo concreto degli oggetti che verranno istanziati. In realtà la questione è ben più ampia.
Si potrebbe incorrere in casi nei quali conosciamo il tipo esatto degli oggetti, ma quest'ultimo potrà cambiare in futuro. Segue che il client sia libero dall'onere di conscere che tipo di oggetti istanziare e il pattern restituisce un oggetto astratto che si realizza poi mediante le classi ereditate dall'entità astratta. Spesso il client guida la creazione dell'oggetto, come nell'esempio mostrato prima, ma ignora i dettagli della sua costruzione.
Il pattern Iterator
Per quanto possa sembrare strano, il pattern Iterator è assimilabile alla famiglia dei Factory Method. Infatti Iterator ci da la possibilità di accedere sequanzialmente agli elementi di una lista, sollevando il chiamante dalla necessità di conoscere quali siano le classi istanziate.
Iterator è un'interfaccia che mette a disposizione tre metodi:
- boolean hasNext() che restituisce true se e solo se è ancora possibile effettuare un'iterazione;
- Object next() che restituisce l'elemento successivo a quello corrente;
- void remove() che si occupa di rimuovere l'elemento corrente dalla lista;
Vediamo un esempio:
import java.io.*; import java.util.*; class Test { public static void main(String[] args) { ArrayListlist = new ArrayList (); list.add("1"); list.add("2"); list.add("3"); list.add("4"); list.add("5"); // Iterator per attraversare la lista Iterator iterator = list.iterator(); System.out.println("List elements : "); while (iterator.hasNext()) System.out.print(iterator.next() + " "); System.out.println(); } }
Otterremo come output:
List elements : A B C D E
Java mette a disposizione una classe ListIterator che permette di attraversare le collezioni di oggetti in entrambe le direzioni, quindi da testa a coda e da coda a testa.
Sintesi
Utilizziamo il Factory Method Pattern quando:
- Una classe non può conoscere in anticipo il tipo esatto degli oggetti da creare;
- La classe conosce il tipo esatto degli oggetti, ma ha bisogno di delegare ad un'entità esterna la loro creazione;
Partecipanti:
- Product: è l'interfaccia dell'oggetto creato dal Factory Method;
- ConcreteProduct: implementa Product;
- Creator: dichiara il Factory Method e restituisce un oggetto di tipo Product;
- ConcreteCreator: specifica il Factory Method e restituisce l'istanza corretta;
Segue che:
- Il codice ha un livello di flessibilità e riusabilità più alto;
- Un uso inappropriato può portare alla generazione di troppe classi;
Ponendo l'attenzione su alcuni dettagli implementativi:
- Creator può essere concreta o astratta, e creare una versione di default del Factory Method;
- Una Factory può costruire oggetti di tipo diverso mediante istruzioni di tipo if-then-else;
Tipicamente viene utilizzato per Logger e applicativi di questo genere.
Alessio Mungelli
Computer Science student at UniTo (University of Turin), Network specializtion, blogger and writer. I am a kind of expert in Java desktop developement with interests in AI and web developement. Unix lover (but not Windows hater). I am interested in Linux scripting. I am very inquisitive and I love learning new stuffs.
Articoli correlati
Introduzione alle CSS Container Queries
Il responsive web design è una componente essenziale dello sviluppo web. Come sviluppatori front-end, dobbiamo preoccuparci continuamente della moltitudine di nuove risoluzioni e dispositivi. Va da se che creare una versione…
Un approccio a Java: switch statement
Ciao a tutti e bentornati! Dopo una pausa, torniamo oggi con un'altra parte del corso introduttivo alla programmazione, parlando di switch statement, conosciuto anche come costrutto di selezione multipla. Intuizione L'idea dello switch statement…
Un approccio a Java: Il ciclo while
Ciao a tutti e bentornati! Dopo aver fatto una breve, ma corposa, introduzione sui cicli, andiamo oggi a vedere finalmente le prime implementazioni che utilizzano quello che abbiamo definito ciclo precondizionale. In Java, come…
Un approccio a Java: I cicli - Introduzione
Ciao a tutti e bentornati! Sino ad ora, abbiamo parlato di variabili e di strutture di selezione, andando a considerare alcuni degli aspetti fondamentali di questi due concetti. Teoricamente, per…
Un approccio a Java: strutture di selezione - casi d'uso
Ciao a tutti e bentornati! Sino ad ora ci siamo preoccupati di fare una carrellata quanto più completa riguardo i concetti fondamentali di cui abbiamo bisogno per approcciarci all'utilizzo delle…
Un approccio a Java: operatori booleani
La volta precedente, abbiamo ampiamente parlato delle variabili booleane, cercando di delineare quali siano le principali operazioni che si possono effettuare proprio a livello pratico. Di tutti i casi esaminati, non abbiamo…
Un approccio a Java: le variabili booleane
Ciao a tutti e bentornati! La volta precedente, ho fatto un'introduzione alle strutture condizionali, definendo il loro funzionamento. Prima di poter dare qualche esempio pratico, è necessario chiarire in che modo ci…
Un approccio a Java: strutture condizionali
Ciao a tutti e bentornati! Le volte precedenti abbiamo introdotto il concetto di variabile, tentando di definire alcuni concetti basilari a riguardo. Alcune situazioni fanno però intuire come il solo concetto…
Un approccio a Java: Le variabili - caso d'uso
Ciao a tutti amici e bentornati! Dopo l'introduzione fatta sulle variabili, cerchiamo di analizzare alcune criticità che si possono presentare in situazioni alquanto comuni. Partiamo quindi analizzando degli esempi pratici. Esempio 1: divisione…
Un approccio a Java: Le variabili
Ciao a tutti e bentornati! Quest'oggi inizieremo un percorso che ci porterà a studiare, ed eventualmente ripassare, quelle che sono le basi della programmazione. Inizieremo parlando di variaibli. Introduzione Chiunque voglia approcciarsi al…
Hashmap con concatenamento: liste di trabocco
In questa breve serie di articoli andremo a vedere com'è possibile realizzare in C la struttura dati Hashmap. Nell'implementazione andremo ad usare le liste doppiamente concatenate come strutture dati ausiliarie. Andiamo…
Java Strutture Dati: Liste Concatenate
Con il 2020 andiamo ad esaminare un nuovo aspetto della programmazione: le strutture dati. Spesso capita a tutti di utilizzare strutture messe a disposizione dai vari linguaggi di programmazione. L'obiettivo sarà…