Abstract Factory Pattern (Creational Design Patterns in Java, Serie per Dev Italiani #5)
Introduzione
Abstract Factory Pattern (Creational Design Patterns in Java, Serie per Dev Italiani #5)

Introduzione
L’Abstract Factory Pattern è un creational pattern che permette di creare famiglie di oggetti correlati senza dipendere dalle loro classi concrete.
Fornisce un’interfaccia comune per generare gruppi di oggetti coerenti appartenenti alla stessa famiglia.
A differenza del Simple Factory e del Factory Method, che si occupano della creazione di singoli oggetti, l’Abstract Factory opera a un livello di astrazione superiore, gestendo intere famiglie di prodotti correlati.
Il Problema alla Base dell’Abstract Factory
In molti sistemi software non è sufficiente creare singoli oggetti, ma interi gruppi di componenti correlati che devono collaborare in modo coerente.
Il problema nasce quando la loro creazione è distribuita nel codice o gestita in modo non strutturato: diventa difficile garantire compatibilità e coerenza tra le varie componenti.
L’Abstract Factory risolve questo problema fornendo un meccanismo per creare famiglie di oggetti correlati senza esporre le classi concrete e senza vincolare il client alle specifiche implementazioni.
Struttura del pattern
- AbstractFactory: Definisce l’interfaccia per la creazione delle famiglie di prodotti correlati, dichiarando i metodi necessari per istanziare ciascun tipo di prodotto senza specificarne le classi concrete.
- ConcreteFactory1: Implementa l’interfaccia AbstractFactory creando una specifica famiglia di prodotti concreti coerenti tra loro.
- ConcreteFactory2: Implementa l’interfaccia AbstractFactory creando una diversa famiglia di prodotti concreti coerenti tra loro.
- AbstractProductA: Definisce l’interfaccia per una specifica tipologia di prodotto, mentre il tipo concreto da utilizzare viene determinato dalla factory che lo istanzia.
- AbstractProductB: Definisce l’interfaccia per una seconda tipologia di prodotto, mentre il tipo concreto da utilizzare viene determinato dalla factory che lo istanzia.
- ConcreteProductA1: Implementa l’interfaccia AbstractProductA e rappresenta la prima variante concreta del prodotto A, appartenente alla famiglia creata da ConcreteFactory1.
- ConcreteProductB1: Implementa l’interfaccia AbstractProductB e rappresenta la prima variante concreta del prodotto B, appartenente alla famiglia creata da ConcreteFactory1.
- ConcreteProductA2: Implementa l’interfaccia AbstractProductA e rappresenta la seconda variante concreta del prodotto A, appartenente alla famiglia creata da ConcreteFactory2.
- ConcreteProductB2: Implementa l’interfaccia AbstractProductB e rappresenta la seconda variante concreta del prodotto B, appartenente alla famiglia creata da ConcreteFactory2.
- Client: Utilizza le astrazioni del pattern (AbstractFactory e AbstractProduct) per creare e lavorare con famiglie di oggetti correlati senza dipendere dalle loro implementazioni concrete.

Figura 1 - UML dell’Abstract Factory Pattern (Clicca per ingrandire)
Esempio Pratico: Implementazione dell’Abstract Factory Pattern
Quando un’applicazione deve integrare più provider di pagamento, come Stripe e PayPal, il rischio è quello di spargere nel codice controlli condizionali, dipendenze concrete e logiche specifiche per ogni provider. Questo approccio rende il sistema più difficile da mantenere, estendere e testare nel tempo.
L’Abstract Factory Pattern aiuta a risolvere questo problema introducendo un livello di astrazione nella creazione degli oggetti. Invece di istanziare direttamente classi concrete come StripePaymentRequest, StripePaymentResponse, PayPalPaymentRequest o PayPalPaymentResponse, il client lavora esclusivamente con interfacce astratte come PaymentRequest e PaymentResponse, delegando la creazione degli oggetti a factory specializzate.
Nel nostro esempio, ogni provider di pagamento espone una propria famiglia coerente di oggetti, come richieste e risposte di pagamento. La StripePaymentProviderFactory crea esclusivamente componenti compatibili con Stripe, mentre la PayPalPaymentProviderFactory produce gli equivalenti per PayPal. In questo modo il client rimane disaccoppiato dalle implementazioni concrete e il sistema può essere esteso con nuovi provider senza modificare il codice esistente.
Il diagramma UML seguente mostra come l’Abstract Factory Pattern organizza le factory e i prodotti del nostro esempio, mantenendo separata la logica di utilizzo dalla logica di creazione degli oggetti.

Figura 2 - Abstract Factory applicato al sistema di pagamento (Ingrandisci)
Implementazione in Java
Vediamo ora come la struttura descritta nel diagramma UML si traduce in codice Java. L’obiettivo è mantenere lo stesso livello di astrazione visto nel modello, utilizzando interfacce per i prodotti e factory concrete per la loro creazione. In questo modo il client rimane completamente indipendente dalle implementazioni specifiche dei vari provider di pagamento.
Definiamo l’Abstract Factory:
public interface PaymentProviderFactory {
PaymentRequest createPaymentRequest();
PaymentResponse createPaymentResponse();
}
Implementiamo la factory concreta responsabile della creazione degli oggetti di pagamento compatibili con Stripe.
public class StripePaymentProviderFactory implements PaymentProviderFactory {
@Override
public PaymentRequest createPaymentRequest() {
return new StripePaymentRequest();
}
@Override
public PaymentResponse createPaymentResponse() {
return new StripePaymentResponse();
}
}
Implementiamo la factory concreta responsabile della creazione degli oggetti di pagamento compatibili con PayPal.
public class PayPalPaymentProviderFactory implements PaymentProviderFactory {
@Override
public PaymentRequest createPaymentRequest() {
return new PayPalPaymentRequest();
}
@Override
public PaymentResponse createPaymentResponse() {
return new PayPalPaymentResponse();
}
}
Definiamo il prodotto astratto relativo alla richiesta di pagamento.
public interface PaymentRequest {
void process();
}
Definiamo il prodotto astratto relativo alla risposta di pagamento.
public interface PaymentResponse {
void handle();
}
Implementiamo la richiesta di pagamento concreta compatibile con Stripe.
public class StripePaymentRequest implements PaymentRequest {
@Override
public void process() {
System.out.println("Processing Stripe payment request");
}
}
Implementiamo la richiesta di pagamento concreta compatibile con PayPal.
public class PayPalPaymentRequest implements PaymentRequest {
@Override
public void process() {
System.out.println("Processing PayPal payment request");
}
}
Implementiamo la risposta di pagamento concreta gestita dal provider Stripe.
public class StripePaymentResponse implements PaymentResponse {
@Override
public void handle() {
System.out.println("Handling Stripe payment response");
}
}
Implementiamo la risposta di pagamento concreta gestita dal provider PayPal.
public class PayPalPaymentResponse implements PaymentResponse {
@Override
public void handle() {
System.out.println("Handling PayPal payment response");
}
}
Il client utilizza la factory per creare dinamicamente i componenti di pagamento senza conoscere le implementazioni concrete dei singoli provider.
public class Client {
private final PaymentRequest request;
private final PaymentResponse response;
public Client(PaymentProviderFactory factory) {
this.request = factory.createPaymentRequest();
this.response = factory.createPaymentResponse();
}
public void execute() {
request.process();
response.handle();
}
public static void main(String[] args) {
PaymentProviderFactory factory = new StripePaymentProviderFactory();
Client client = new Client(factory);
client.execute();
}
}
Conclusione
L’Abstract Factory Pattern risulta particolarmente utile quando un sistema deve gestire famiglie di oggetti correlati senza vincolare il client a implementazioni concrete.
Nel nostro esempio, questo approccio separa la creazione dei componenti di pagamento (Stripe e PayPal) dalla loro logica di utilizzo, riducendo l’accoppiamento e semplificando l’estensione del sistema.
Il vero valore emerge quando aumentano le varianti: aggiungere un nuovo provider significa introdurre una nuova factory e un nuovo insieme di prodotti, senza modificare il codice esistente. Questo è uno dei principi fondamentali della progettazione software: estendere senza rompere ciò che già funziona.
메타데이터
- post_id
- 24fd002ead29
- slug
- creational-design-patterns-in-java-serie-per-dev-italiani-5-abstract-factory-pattern-24fd002ead29
- url
- https://medium.com/techtrends-digest/creational-design-patterns-in-java-serie-per-dev-italiani-5-abstract-factory-pattern-24fd002ead29
- canonical_url
- https://medium.com/techtrends-digest/creational-design-patterns-in-java-serie-per-dev-italiani-5-abstract-factory-pattern-24fd002ead29
- author_url
- https://medium.com/@leonardo.percoco_65603
- status
- ok
- fetched_at
- 2026-06-27 10:07:59