Factory Method: creare senza conoscere — articolo

> Factory Method: creare senza conoscere

Pillar article sul Factory Method Pattern nel framework: make() statico, SchemaBuilder per driver, e il principio di inversione delle dipendenze applicato alla creazione.

Luigi Iadicola
~5 min lettura
#PHP 8.4 #ORM #Architettura #Design Pattern
Factory Method: creare senza conoscere
Factory Method: creare senza conoscere

new e una dipendenza concreta

Ogni volta che scrivi new MysqlSchemaBuilder(), stai creando una dipendenza concreta. Il codice chiamante ora sa esattamente quale classe istanziare — e se domani devi supportare PostgreSQL, devi trovare e modificare ogni punto dove quel new compare. Il Factory Method risolve questo problema delegando la creazione a un metodo che le sottoclassi possono sovrascrivere, o che una logica interna puo personalizzare in base al contesto.

Il pattern e sottilmente diverso dall'Abstract Factory: non crea famiglie di oggetti correlati, ma singoli oggetti il cui tipo concreto dipende dal contesto. In Soft PHP MVC, SchemaBuilder::make() ispeziona il driver configurato e restituisce il builder corretto — MysqlSchemaBuilder, PostgresSchemaBuilder, SqliteSchemaBuilder, MariaDbBuilder — senza che il chiamante debba saperlo. La decisione e centralizzata in un unico punto, non dispersa nel codice applicativo.

Cos'e il Factory Method: definizione e struttura

Il Gang of Four definisce il Factory Method come un pattern creazionale che "definisce un'interfaccia per creare un oggetto, ma lascia alle sottoclassi la decisione su quale classe istanziare. Il Factory Method permette a una classe di rinviare l'istanziazione alle sottoclassi". Gli attori sono: il Creator (la classe con il metodo factory), il ConcreteCreator (la sottoclasse che decide il tipo concreto), il Product (l'interfaccia del prodotto), e il ConcreteProduct (l'implementazione specifica).

Il cuore del pattern e la separazione tra la logica di creazione e la logica di utilizzo. Chi usa l'oggetto non sa come e stato creato; chi lo crea non sa come verra usato. Questa separazione permette di cambiare le regole di creazione senza impattare il codice che usa l'oggetto — e viceversa. Il Dependency Inversion Principle nella sua applicazione piu diretta alla creazione degli oggetti.

Il pattern make() nel framework

Il make() statico e il Factory Method adottato sistematicamente in Soft PHP MVC. CsrfService::make(), EncryptionService::make(), SessionService::make() — ogni service espone un metodo factory che incapsula la logica di creazione. Oggi alcuni restituiscono semplicemente new static(); domani potrebbero restituire un singleton, leggere una configurazione, o risolvere da un container. Il punto di creazione e uno solo, e puo evolvere senza ripercussioni sul resto del codice.

La forza del Factory Method e nella promessa che fa al codice chiamante: "non ti preoccupare del come, dimmi solo cosa ti serve". SchemaBuilder::make() potrebbe creare l'oggetto con new, pescarlo da una cache, o costruirlo con dieci dipendenze — il chiamante non lo sa e non deve saperlo. Questa ignoranza deliberata e il cuore del disaccoppiamento. Il Factory Method non nasconde complessita — la isola in un punto dove puo essere gestita senza contaminare il codice che consuma il prodotto.

Vantaggi concreti del pattern make()

L'adozione sistematica di make() nel framework porta vantaggi misurabili:

  • Punto di creazione unico — ogni modifica alla logica di istanziazione avviene in un solo metodo
  • Evoluzione senza breaking change — passare da new static() a singleton o pool richiede zero modifiche al codice chiamante
  • Naming semanticomake() comunica "sto creando un'istanza pronta all'uso", diverso da un costruttore generico
  • Testabilita — il factory method puo essere sovrascritto in una sottoclasse di test per restituire mock o stub
  • Consistenza — ogni service si istanzia allo stesso modo, riducendo il carico cognitivo per chi legge il codice

Factory Method vs costruttore: una questione di nomi

Un costruttore non puo avere un nome semantico. new Connection($config) non dice se stai creando una connessione nuova o riutilizzando una esistente. Connection::fromConfig($config) e Connection::forTesting() sono Factory Method con nomi che comunicano l'intento. Il nome e documentazione che il compilatore verifica — non puo diventare obsoleto come un commento. Un factory method ben nominato e il miglior tipo di documentazione: preciso, verificabile, impossibile da ignorare.

In PHP, i named constructor — metodi statici che restituiscono new static(...) — sono la forma piu idiomatica del Factory Method. Il framework ne usa diversi: Migration::table() per creare migrazioni, Seeder::table() per creare seeder, Column::make() per definire colonne. Ogni nome racconta cosa stai costruendo e perche. La proliferazione di named constructor non e un segno di over-engineering — e un segno di API che comunicano con chiarezza.

Factory Method e PHP 8.4

Le novita di PHP 8.4 rendono il Factory Method ancora piu espressivo. Le property hooks permettono di validare i parametri nel momento dell'assegnazione, riducendo la logica nel costruttore. I tipi di intersezione permettono di restituire oggetti che soddisfano piu interfacce contemporaneamente. E le closure di prima classe rendono possibile passare factory method come callable, abilitando pattern di lazy creation e deferred resolution che prima richiedevano classi dedicate.

Quando il factory e troppo

Non ogni new ha bisogno di un factory. Value object semplici, DTO, eccezioni — crearli direttamente e perfettamente legittimo. Il Factory Method serve quando: il tipo concreto puo variare, la logica di creazione e complessa, o vuoi un punto di estensione per il futuro. Se la classe non cambiera mai e il costruttore ha zero o un parametro, new e la scelta giusta. L'over-engineering e un costo reale, non solo teorico.

Ecco una guida pratica per decidere:

  • Usa new — value object, DTO, eccezioni, classi con costruttore semplice e tipo fisso
  • Usa make() — service, repository, handler, classi con logica di creazione non banale
  • Usa named constructor — quando lo stesso tipo puo essere creato da input diversi (fromArray, fromRequest, forTesting)
  • Usa Abstract Factory — quando devi creare famiglie di oggetti correlati che variano insieme

Il pragmatismo e la differenza tra conoscere i pattern e sapere quando applicarli. Un codebase dove ogni oggetto passa attraverso un factory e un codebase over-engineered. Un codebase dove i punti di creazione critici hanno un factory e il resto usa new e un codebase che bilancia flessibilita e semplicita — e quello e l'obiettivo di ogni architettura PHP ben progettata.

altri articoli