
Il Factory Method è un design pattern creazionale che definisce un’interfaccia per creare un oggetto, lasciando alle sottoclassi la decisione su quale classe concreta istanziare. Questo pattern consente di delegare la responsabilità della creazione degli oggetti, favorendo disaccoppiamento ed estensibilità.
Scenario
In molti contesti software, è necessario istanziare oggetti che condividono un’interfaccia comune, ma che presentano comportamenti o configurazioni differenti. Se il codice client si occupa direttamente di creare questi oggetti, si genera un forte accoppiamento alle classi concrete, rendendo il sistema difficile da estendere e mantenere.
Immaginiamo un’applicazione per il controllo dei forni industriali in un’azienda specializzata nei trattamenti termici dei metalli, inizialmente pensata per gestire un unico modello con connessione seriale. Man mano che arrivano nuovi forni, prima con comunicazione TCP/IP e poi con OPC UA, la complessità del codice aumenta rapidamente. Il codice client, incaricato di impostare la temperatura, deve ora contenere al suo interno la logica per istanziare, configurare e gestire le connessioni specifiche per ogni tipo di forno. Questo porta a potenziale duplicazione di codice e rende più complessa la manutenzione e l’introduzione di nuove tipologie di forno. Serve una soluzione per centralizzare la creazione degli oggetti, disaccoppiando il codice client dalle implementazioni concrete dei vari tipi di connessione. Ed è qui che il Factory Method si rivela prezioso: ci consente di spostare la responsabilità di creazione in una gerarchia dedicata, astraendo il processo dietro un’interfaccia comune. In questo modo, tornando all’esempio, possiamo introdurre nuovi tipi di forno senza toccare il codice client.
Nel prossimo capitolo vedremo nel dettaglio come funziona questo pattern e come aiuta a mantenere il codice flessibile, scalabile e più semplice da testare.
Soluzione
ll Factory Method risolve i problemi visti sopra introducendo una classe base che definisce un metodo factory astratto. Le sottoclassi implementano il metodo per restituire istanze concrete specifiche.
Il codice client (cioè quello che chiama il metodo factory) lavora con il tipo base e non sa qual è l’effettivo tipo di oggetto che sta utilizzando. In questo modo, l’applicazione rimane flessibile, estensibile e più facile da testare.
Struttura
Il diagramma seguente mostra classi e interfacce coinvolte nel Factory Method.

I soggetti coinvolti sono:
- L’interfaccia Product. Definisce il tipo di oggetto ritornato dal factory method. Product potrebbe anche essere una classe. Ciò non cambia nulla ai concetti e alla struttura del pattern.
- ConcreteProduct 1, 2, etc. Sono classi concrete che implementano l’interfaccia Product.
- Creator. Definisce (cioè contiene) il factory method CreateProduct() che ritorna un oggetto di tipo Product.
- ConcreteCreator 1, 2, etc. Sono le classi che eseguono l’override del metodo factory con il quale ritornano uno degli oggetti di ConcreteProduct.
Vantaggi e svantaggi
Vantaggi
I vantaggi derivanti dall’uso del Factory Method design pattern sono:
- Indipendenza dalle classi concrete: il codice client interagisce con un’interfaccia comune, senza conoscere le classi concrete, rendendolo più flessibile e indipendente da implementazioni concrete.
- Aderenza all’Open-Closed Principle: È possibile aggiungere nuovi tipi di prodotti senza modificare il codice esistente.
- Separazione delle responsabilità: Il codice client si concentra sull’utilizzo dell’oggetto, non sulla sua creazione.
Svantaggi
- Maggiore complessità: l’introduzione di interfacce e sottoclassi può essere sovrabbondante per semplici scenari.
- Frammentazione: la logica di creazione è sparsa tra diverse classi.
Quando usare il Factory Method
Il Factory Method design pattern è particolarmente utile nei seguenti scenari:
- Quando non si conoscono in anticipo le classi concrete da istanziare: se il codice deve lavorare con oggetti la cui implementazione dipende da condizioni che variano nel tempo o a runtime, il Factory Method permette di delegare la decisione di istanziazione a sottoclassi specifiche.
- Quando una classe deve delegare alle sottoclassi la scelta dell’oggetto da creare: questo accade spesso in contesti estendibili, in cui si vuole permettere a chi estende una classe di personalizzare anche il tipo di oggetto prodotto.
- Quando il processo di creazione è complesso o soggetto a variazioni: ad esempio, se l’oggetto da istanziare dipende da configurazioni, parametri esterni o contesti runtime (come il tipo di protocollo, file di configurazione, ecc.).
- Quando si vuole disaccoppiare il codice client dalla conoscenza delle classi concrete: in questo modo il codice client lavora con interfacce o classi astratte, migliorando l’estensibilità e riducendo l’impatto delle modifiche future.
Quando evitare il Factory Method
In generale, conviene evitare il Factory Method quando:
- Le classi concrete del prodotto non cambiano mai. Se hai una sola implementazione di un’interfaccia di prodotto e non prevedi di aggiungerne altre in futuro, l’introduzione di un Factory Method potrebbe aggiungere complessità non necessaria al codice. Un’istanzazione diretta della classe concreta sarebbe più semplice e immediata.
- La creazione del prodotto è estremamente semplice e non richiede alcuna logica di inizializzazione complessa. Se creare un oggetto prodotto è un’operazione banale che non coinvolge configurazioni, dipendenze o passaggi particolari, il Factory Method potrebbe essere superfluo.
- In contesti con forti vincoli di performance e la creazione del prodotto è un’operazione critica. L’introduzione di un’ulteriore indirezione tramite il Factory Method, per quanto minima, può comportare un lieve overhead rispetto alla creazione diretta degli oggetti. In applicazioni con requisiti di latenza estremamente stringenti, anche questa piccola differenza può diventare rilevante. Tuttavia, si tratta spesso di una micro-ottimizzazione che raramente rappresenta un problema concreto nella maggior parte dei contesti applicativi.
Esempio reale
Riprendiamo lo scenario dei forni industriali visto sopra: abbiamo modelli con protocolli di comunicazione diversi, ma comportamenti simili. Vediamo ora come applicare il Factory Method per gestire questa variabilità nel codice. Nel dettaglio, l’applicazione deve:
- Gestire sia forni vecchi (che utilizzano una connessione seriale), sia forni più recenti (che usano invece una connessione TCP/IP). La logica è comune a tutti i forni (apertura connessione, impostazione temperatura, chiusura connessione), ma cambiano i dettagli di comunicazione.
- Deve permettere di aggiungere nuovi tipi di forno senza dover mettere mano al codice client esistente.
Lo schema seguente mostra una possibile soluzione che utilizza il Factory Method.

Nel dettaglio, abbiamo:
- L’interfaccia IDeviceConnection, che rappresenta il tipo restituito dal Factory Method. Espone i metodi Open(), SetTemperature() e Close, indipendentemente dal tipo di connessione utilizzato (seriale, TCP, ecc.). Rispetto alla struttura generale, IDeviceConnection corrisponde all’interfaccia Product
- Le classi SerialConnection e TcpConnection, che implementano IDeviceConnection fornendo il comportamento specifico per ciascun tipo di connessione. Rispetto alla struttura generale, queste classi corrispondono alle classi ConcreteProduct 1 e 2.
- La classe astratta OvenController, che definisce il Factory Method CreateConnection() e un metodo SetOvenTemperature() che incapsula la logica: apertura connessione, scrittura temperatura, chiusura. Rispetto alla struttura generale, OvenController corrisponde alla classe Creator.
- Le sottoclassi concrete LegacyOvenController e ModernOvenController, che sovrascrivono CreateConnection() per restituire rispettivamente una SerialConnection o una TcpConnection.Rispetto alla struttura generale, queste classi corrispondono alle classi ConcreteCreator 1 e 2.
Il codice client lavora solo con oggetti di tipo OvenController. Non deve preoccuparsi di quale connessione venga usata: sarà il Factory Method, definito nel creatore concreto, a fornire l’istanza corretta. Questo rende il codice più modulare, aperto all’estensione e più semplice da mantenere.
Questo approccio consente di estendere facilmente il supporto a nuovi tipi di connessione (ad esempio connessioni via OPC-UA) semplicemente creando nuovi creatori concreti, senza toccare la logica del controller o il codice client.
LabVIEW
Vediamo ora l’implementazione in LabVIEW dell’esempio visto sopra. Trovate il codice completo qui.
L’immagine seguente mostra l’implementazione del Factory Method: a sinistra LegacyOvenController, a destra ModernOvenController. In questo esempio, ciascun creatore concreto restituisce semplicemente l’istanza appropriata di IDeviceConnection: LegacyOvenController ritorna un oggetto SerialConnection, mentre ModernOvenController ritorna un oggetto TcpConnection.

Nella pratica, l’oggetto creato può richiedere parametri di inizializzazione specifici (ad esempio, la porta seriale o l’indirizzo IP). Anche questa logica dovrebbe essere incapsulata all’interno del Factory Method, così da mantenere l’astrazione e non esporre dettagli di costruzione al codice client.
In questo modo, si evita di disperdere responsabilità nel codice client e si rispetta il principio di singola responsabilità (SRP): la logica di creazione rimane confinata nel punto giusto, rendendo il sistema più manutenibile e meno accoppiato.
L’immagine seguente mostra invece il metodo SetOvenTemperature della classe OvenController. Questo è un approccio molto comune: si invoca il Factory Method (CreateConnection), che restituisce un’istanza corretta e già inizializzata del tipo IDeviceConnection. Una volta ottenuta la connessione, il metodo lavora esclusivamente con l’interfaccia, senza preoccuparsi dei dettagli di implementazione.
SetOvenTemperature incapsula una sequenza ben definita di operazioni: apertura della connessione, invio del comando di modifica temperatura, e chiusura della connessione. Questa struttura ricalca il pattern del Template (che approfondiremo in uno dei prossimi post), spesso utilizzato in combinazione proprio con il Factory Method. Il Template definisce il flusso generale dell’algoritmo, delegando la creazione degli oggetti specifici (in questo caso, la connessione) al Factory Method. Il risultato è un codice ben organizzato, estendibile e con responsabilità chiaramente separate.

L’immagine seguente, infine, mostra il codice client. Qui vengono istanziati sia LegacyOvenController sia ModernOvenController. Nonostante rappresentino due tipi diversi di forno, il client può interagire con entrambi nello stesso modo, semplicemente chiamando il metodo SetOvenTemperature. Questo dimostra uno dei principali vantaggi del Factory Method: il codice client rimane indipendente dalle classi concrete utilizzate, favorendo riuso, testabilità e apertura all’estensione.

Nel prossimo paragrafo, vedremo l’implementazione concreta in C#.
C#
Vediamo ora, per completezza, l’implementazione dello stesso esempio in C#. Il codice completo è disponibile qui.
Questa è la definizione IDeviceConnection e le sue implementazioni.
{
void Open();
void Close();
void SetTemperature(double temperature);
}
public class SerialConnection : IDeviceConnection
{
public void Open() => Console.WriteLine("Serial connection opened");
public void Close() => Console.WriteLine("Serial connection closed");
public void SetTemperature(double temperature) =>
Console.WriteLine($"[Serial] Temperature set to {temperature} °C");
}
public class TCPConnection : IDeviceConnection
{
public void Open() => Console.WriteLine("TCP connection opened");
public void Close() => Console.WriteLine("TCP connection closed");
public void SetTemperature(double temperature) =>
Console.WriteLine($"[TCP] Temperature set to {temperature} °C");
}
Di seguito invece è riportata la definizione e l’implementazione del Factory Method:
public abstract class OvenController
{
public void SetOvenTemperature(double temperature)
{
var connection = CreateConnection();
connection.Open();
connection.SetTemperature(temperature);
connection.Close();
}
// This is the actual Factory Method definition
protected abstract IDeviceConnection CreateConnection();
}
public class LegacyOvenController : OvenController
{
// Factory method implementation
protected override IDeviceConnection CreateConnection()
=> new SerialConnection();
}
public class ModernOvenController : OvenController
{
// Factory method implementation
protected override IDeviceConnection CreateConnection()
=> new TCPConnection();
}
Conclusioni
In questo post abbiamo esaminato il pattern Factory Method, un design pattern creazionale, con un’attenzione particolare al principio di separazione tra creazione e utilizzo degli oggetti. Abbiamo visto come questo approccio favorisca una progettazione più pulita, modulare e conforme ai principi SOLID, in particolare al principio di responsabilità singola.
Il Factory Method è spesso un primo passo per affrontare scenari in cui la flessibilità nella creazione degli oggetti diventa cruciale. In sistemi più complessi, può evolvere naturalmente verso pattern come Abstract Factory, utile quando le famiglie di oggetti da istanziare sono molteplici.
Infine, vale la pena sottolineare come il Factory Method venga spesso usato in accoppiata con il pattern Template Method, quando serve definire una struttura di operazioni e algoritmi comune lasciando ai sottotipi la personalizzazione di specifici passaggi, inclusa la creazione degli oggetti.
Il codice di esempio descritto in questo articolo è disponibile a questi link: LabVIEW e C#.
Nel prossimo post vedremo come il pattern Abstract Factory estenda queste idee, offrendo un livello superiore di astrazione nella creazione degli oggetti.
Quando si scrive codice orientato agli oggetti, comprendere bene questi pattern non è solo una questione accademica: è ciò che spesso fa la differenza tra un progetto che si evolve con facilità e uno che si ingessa al primo cambiamento.