Gli sviluppatori che creano applicazioni Laravel sono incoraggiati ad abbandonare la parola chiave new all'interno dei propri controller e a passare al pattern Factory Method. Spostando la creazione degli oggetti in factory dedicate, il codice diventa più facile da estendere, testare e mantenere — vantaggi che diventano fondamentali quando i progetti crescono oltre una manciata di endpoint.
Perché la parola chiave new è un rischio nascosto
Quando un controller contiene una riga come
$processor = new StripePaymentProcessor($config);
il controller risulta ora strettamente accoppiato alla classe StripePaymentProcessor. Ogni punto che necessita di un processore di pagamenti ripete quella riga, disperdendo la logica di costruzione in tutto il codice sorgente. Se in seguito il processore dovesse richiedere un logger, un gestore di cache o un formato di configurazione diverso, ogni singolo punto dovrà essere aggiornato. Il risultato è un codice fragile che resiste ai cambiamenti ed è difficile da simulare (mock) nei test unitari.
L'intervento del pattern Factory Method
Il pattern Factory Method sostituisce l'istanziazione diretta con un metodo che restituisce un'istanza di una determinata interfaccia. Il controller dipende dall'interfaccia, mentre una classe factory sa quale classe concreta costruire e come assemblarne le dipendenze. Tutta la logica di creazione risiede in un unico punto, quindi l'aggiunta di una nuova dipendenza o la sostituzione di un'implementazione richiede modifiche solo alla factory.
Vantaggi principali
- Accoppiamento debole (Loose coupling) – I controller lavorano su astrazioni, non su classi concrete.
- Costruzione centralizzata – Modificare il processo di costruzione richiede l'editing di un singolo file.
- Testabilità – Le factory possono essere simulate o sostituite con dei mock, permettendo test isolati del controller.
- Principio Open/Closed – Nuove funzionalità (ad esempio, un nuovo gateway di pagamento) possono essere aggiunte senza toccare il codice esistente del controller.
Un'analogia con il bar
Immaginate un barista che deve macinare i chicchi, montare il latte e versare l'acqua manualmente per ogni ordine, usando una lunga serie di istruzioni if-else. Se la ricetta del latte cambia, ogni barista deve imparare nuovamente i passaggi. Una macchina del caffè che riceve il tipo di ordine e gestisce la preparazione internamente risolve il problema: il barista comunica semplicemente alla macchina ciò che serve, e la macchina incapsula tutti i passaggi. Aggiornare la ricetta ora significa regolare la macchina, non ogni singolo barista.
Laravel si affida già alle factory
I componenti core di Laravel illustrano il pattern in azione:
- Database –
ConnectionFactorydecide se creare una connessione MySQL o PostgreSQL. - Code (Queues) –
QueueManagercrea driver per Redis, SQS o altri back-end. - Filesystem –
FilesystemManagerproduce istanze di disco locali o S3. - Mail –
MailManagerrisolve driver SMTP, Mailgun o altri driver di posta.
Se il framework si affida alle factory per questi servizi critici, anche il codice personalizzato dovrebbe fare lo stesso.
Quando introdurre una factory
Usa una factory quando:
- La creazione dell'oggetto comporta molteplici passaggi di configurazione o servizi esterni.
- Esistono diverse implementazioni intercambiabili (diversi gateway di pagamento, provider di archiviazione, ecc.).
- Si prevede che l'elenco delle possibili implementazioni crescerà.
Evita una factory quando:
- La costruzione è una singola chiamata
newimmutabile senza configurazioni extra. - Verrà utilizzata una sola implementazione, rendendo l'astrazione extra un overhead non necessario.
Guida rapida: refactoring di un controller di pagamento
- Definisci un'interfaccia –
PaymentProcessorInterfacecon un metodoprocess(array $data). - Implementa le classi concrete –
StripePaymentProcessor,PayPalPaymentProcessor, ognuna delle quali soddisfa l'interfaccia. - Crea una factory –
PaymentProcessorFactorycon un metodomake(string $driver): PaymentProcessorInterface. All'interno, usa unoswitcho una mappa per restituire la classe appropriata, iniettando i servizi richiesti dal service container di Laravel. - Inietta la factory – Nel costruttore del controller, usa il type-hint
PaymentProcessorFactory. Laravel la risolverà automaticamente. - Usa la factory –
$processor = $this->processorFactory->make('stripe'); $processor->process($request->all());
Ora il controller non menziona mai new o un processore concreto. Aggiungere un nuovo gateway significa creare una classe ed estendere la mappa della factory: nessun cambiamento al controller.
Possibili svantaggi e come mitigarli
La critica principale alle factory è l'aggiunta di indirezione, che può risultare prolissa per oggetti banali. L'over-engineering di un semplice servizio può gonfiare la codebase senza reali benefici. La chiave è valutare la complessità della costruzione: se un oggetto è un semplice contenitore di dati senza dipendenze, un new diretto può essere accettabile. Inoltre, le factory possono diventare un deposito per logica non pertinente. Mantenere le factory focalizzate sulla creazione di oggetti e delegare la configurazione a service provider dedicati preserva la chiarezza.
A cosa prestare attenzione successivamente
- Service container bindings – Il container di Laravel può risolvere le factory automaticamente se si associa l'interfaccia alla classe della factory in un service provider.
- Auto-discovery – Alcuni pacchetti espongono le proprie factory; prestare attenzione alle collisioni di nomi.
- Testing strategies – Quando si eseguono unit test sui controller, sostituisci la factory con un mock che restituisca un processor stubbato, garantendo che i test rimangano veloci e isolati.
In sintesi
Sostituire le chiamate new sparse con una factory ben posizionata centralizza la costruzione, riduce l'accoppiamento e allinea il codice personalizzato con la filosofia di design di Laravel. Per i team che prevedono una crescita — nuovi provider di pagamento, backend di storage o qualsiasi componente in stile plug-in — il pattern Factory Method è un investimento a basso costo che ripaga in termini di flessibilità e sicurezza. Il tuo "te stesso del futuro" e chiunque erediterà il codice ti ringrazieranno.
