Design Pattern Fondamentali in Java
I Design Pattern sono soluzioni riutilizzabili a problemi comuni. Conoscerli permette di scrivere codice più elegante, manutenibile e comunicare efficacemente con altri sviluppatori.
Pattern Trattati
- Singleton - Istanza unica
- Factory Method - Creazione delegata
- Builder - Costruzione complessa
- Strategy - Algoritmi intercambiabili
- Observer - Notifiche di eventi
Singleton
// Singleton classico (thread-safe con enum)
public enum DatabaseConnection {
INSTANCE;
private Connection connection;
DatabaseConnection() {
// Inizializzazione connessione
}
public Connection getConnection() {
return connection;
}
}
// Uso
DatabaseConnection.INSTANCE.getConnection();
// Singleton con double-checked locking
public class ConfigurationManager {
private static volatile ConfigurationManager instance;
private Properties config;
private ConfigurationManager() {
config = new Properties();
// Carica configurazione
}
public static ConfigurationManager getInstance() {
if (instance == null) {
synchronized (ConfigurationManager.class) {
if (instance == null) {
instance = new ConfigurationManager();
}
}
}
return instance;
}
public String getProperty(String key) {
return config.getProperty(key);
}
}
Factory Method
// Interfaccia prodotto
interface Documento {
void apri();
void salva();
}
// Prodotti concreti
class DocumentoPdf implements Documento {
public void apri() { System.out.println("Apri PDF"); }
public void salva() { System.out.println("Salva PDF"); }
}
class DocumentoWord implements Documento {
public void apri() { System.out.println("Apri Word"); }
public void salva() { System.out.println("Salva Word"); }
}
// Factory
class DocumentoFactory {
public static Documento crea(String tipo) {
return switch (tipo.toUpperCase()) {
case "PDF" -> new DocumentoPdf();
case "WORD" -> new DocumentoWord();
default -> throw new IllegalArgumentException("Tipo non supportato");
};
}
}
// Uso
Documento doc = DocumentoFactory.crea("PDF");
doc.apri();
Builder
public class Studente {
private final String nome;
private final String cognome;
private final String email;
private final int eta;
private final String corso;
private Studente(Builder builder) {
this.nome = builder.nome;
this.cognome = builder.cognome;
this.email = builder.email;
this.eta = builder.eta;
this.corso = builder.corso;
}
// Getters...
public static class Builder {
private final String nome; // Obbligatorio
private final String cognome; // Obbligatorio
private String email;
private int eta;
private String corso;
public Builder(String nome, String cognome) {
this.nome = nome;
this.cognome = cognome;
}
public Builder email(String email) {
this.email = email;
return this;
}
public Builder eta(int eta) {
this.eta = eta;
return this;
}
public Builder corso(String corso) {
this.corso = corso;
return this;
}
public Studente build() {
return new Studente(this);
}
}
}
// Uso
Studente studente = new Studente.Builder("Mario", "Rossi")
.email("mario@email.com")
.eta(22)
.corso("Informatica")
.build();
Strategy
// Interfaccia strategia
interface StrategiaOrdinamento {
void ordina(int[] array);
}
// Strategie concrete
class BubbleSort implements StrategiaOrdinamento {
public void ordina(int[] array) {
// Implementazione bubble sort
System.out.println("Ordinamento con Bubble Sort");
}
}
class QuickSort implements StrategiaOrdinamento {
public void ordina(int[] array) {
// Implementazione quick sort
System.out.println("Ordinamento con Quick Sort");
}
}
// Contesto
class Ordinatore {
private StrategiaOrdinamento strategia;
public void setStrategia(StrategiaOrdinamento strategia) {
this.strategia = strategia;
}
public void eseguiOrdinamento(int[] array) {
strategia.ordina(array);
}
}
// Uso
Ordinatore ordinatore = new Ordinatore();
ordinatore.setStrategia(new QuickSort());
ordinatore.eseguiOrdinamento(new int[]{5, 2, 8, 1});
Observer
import java.util.*;
// Interfaccia observer
interface Observer {
void aggiorna(String messaggio);
}
// Subject (Observable)
class CanaleTelegramico {
private List<Observer> iscritti = new ArrayList<>();
private String ultimoMessaggio;
public void iscrivi(Observer o) {
iscritti.add(o);
}
public void disiscrivi(Observer o) {
iscritti.remove(o);
}
public void pubblicaMessaggio(String messaggio) {
this.ultimoMessaggio = messaggio;
notificaTutti();
}
private void notificaTutti() {
for (Observer o : iscritti) {
o.aggiorna(ultimoMessaggio);
}
}
}
// Observer concreto
class Utente implements Observer {
private String nome;
public Utente(String nome) {
this.nome = nome;
}
public void aggiorna(String messaggio) {
System.out.println(nome + " ha ricevuto: " + messaggio);
}
}
// Uso
CanaleTelegramico canale = new CanaleTelegramico();
canale.iscrivi(new Utente("Mario"));
canale.iscrivi(new Utente("Luigi"));
canale.pubblicaMessaggio("Nuovo articolo pubblicato!");
Quando Usare i Pattern
Guida alla Scelta
| Pattern | Quando Usarlo |
|---|---|
| Singleton | Una sola istanza globale (config, connection pool) |
| Factory | Creazione oggetti basata su parametri |
| Builder | Oggetti con molti parametri opzionali |
| Strategy | Comportamento che varia a runtime |
| Observer | Notifiche di cambiamento stato |







