Factory pattern: tutto quello che c’è da sapere sul factory method pattern
Dalla sua pubblicazione nel 1994 sono state vendute più di 500.000 copie di Design Patterns: Elements of Reusable Object-Oriented Software. Questo libro per sviluppatori software descrive 23 diversi tipi di design pattern, conosciuti come i design pattern della Gang of Four; il nome deriva dal gruppo di quattro autori Erich Gamma, John Vlissides, Ralph Johnson e Richard Helm. Tra le numerose strategie di design racchiuse in questa opera si annovera anche il cosiddetto factory method (“metodo di fabbrica”), detto anche factory pattern, che permette a una classe di delegare la creazione di oggetti alle sottoclassi.
Che cos’è il factory pattern?
Il factory method pattern descrive un approccio di programmazione con il quale creare oggetti senza bisogno di dover specificare la loro classe. Questo permette di cambiare comodamente e in maniera flessibile l’oggetto creato. Lo sviluppatore sceglie se specificare il factory method in un’interfaccia e quindi implementarlo come classe figlio o come classe base ed eventualmente sovrascriverlo dalle classi derivate. Questo metodo opera a livello della classe costruttore standard per separare la costruzione degli oggetti dagli oggetti stessi e permettere così l’utilizzo dei principi SOLID.
I principi SOLID sono una parte dei principi del design orientato agli oggetti, che hanno lo scopo di migliorare i processi di sviluppo dei software orientati agli oggetti. L’acronimo “SOLID” sta per i seguenti cinque principi:
- Single responsibility principle: ogni classe deve possedere un’unica responsabilità.
- Open/closed principle: le unità del software devono essere estendibili senza modificarne il comportamento.
- Liskov substitution principle: una classe derivata deve sempre poter essere sostituita dalla sua classe base.
- Interface segregation principle: le interfacce devono corrispondere perfettamente alle richieste dei client che effettuano l’accesso.
- Dependency inversion principle: le classi con un livello di astrazione elevato non devono mai dipendere da classi con un livello di astrazione più basso.
Si parla quindi di factory method ma anche di factory pattern o factory design pattern, nonostante queste versioni non vengano mai menzionate nell’opera della GoF. Oltre al già menzionato factory method pattern, sfogliando il libro trovate solamente l’abstract factory pattern, utile a definire l’interfaccia per la creazione di una famiglia di oggetti le cui classi concrete vengono definite soltanto durante l’esecuzione.
Qual è lo scopo del factory design pattern?
Il factory pattern serve a risolvere un problema di fondo durante l’istanziazione, ossia la creazione di un oggetto concreto di una classe, nell’ambito della programmazione orientata agli oggetti. Creare un oggetto direttamente all’interno della classe di cui l’oggetto ha bisogno, è possibile ma molto poco flessibile. Infatti, la classe viene collegata all’oggetto preciso, rendendo impossibile modificare l’istanziazione, indipendentemente dalla classe. Un codice di questo tipo può essere evitato grazie al factory pattern che definisce un’operazione separata per la creazione dell’oggetto – il cosiddetto metodo factory – sostituendo la classe costruttore nella creazione dell’oggetto.
Factory pattern: diagramma UML del modello Factory
Nei software adatti all’impiego del factory design pattern, il codice dell’oggetto da creare (definito in queste circostanze anche come “prodotto”) viene esternalizzato separatamente in una propria classe. Questa classe astratta chiamata anche “creator” o “factory” delega l’istanziazione dell’oggetto a una sottoclasse (ConcreteCreator), la quale decide infine che prodotto creare. A questo scopo il ConcreteCreator adopera il metodo createProduct() e fornisce in risposta un ConcreteProduct, ampliabile dal Creator utilizzando un codice di creazione, prima che il prodotto finito venga trasmesso all’interfaccia.
Il seguente diagramma di classe UML aiuta a chiarire il funzionamento del factory pattern, riassumendo graficamente le relazioni e i processi descritti.
Vantaggi e svantaggi del factory design pattern
Con il factory pattern l’esecuzione di un metodo di programma è completamente separata dall’implementazione di nuove classi, il che porta con sé alcuni vantaggi. Questo ha un effetto particolarmente positivo sull’ampliabilità del software: le istanze factory possiedono un elevato grado di autonomia e permettono l’aggiunta di nuove classi durante l’esecuzione senza alterare in alcun modo l’applicazione. È sufficiente implementare le interfacce factory e successivamente istanziare il Creator (passando per il ConcreteCreator).
Un ulteriore vantaggio consiste nella buona testabilità delle componenti factory. Se, ad esempio, un Creator implementa tre classi, la loro funzionalità può essere testata singolarmente e indipendentemente dalla classe in esecuzione. Per farlo, è sufficiente assicurarsi che le classi richiamino correttamente il Creator, anche nel caso in cui venissero apportate delle modifiche al software in un secondo momento. Altrettanto vantaggiosa è la possibilità di assegnare ai metodi di fabbrica un nome significativo, possibilità non concessa alla classe costruttore.
Il grande punto debole del factory design pattern è che la sua applicazione porta a un aumento delle classi coinvolte, in quanto ogni ConcreteProduct richiede sempre anche un ConcreteCreator. L’approccio factory è vantaggioso per la possibilità di espansione del software, ma presenta degli svantaggi per quanto riguarda l’impegno necessario. Se deve essere aggiunta una famiglia di prodotti, non deve essere adeguata solamente l’interfaccia, ma anche tutte le classi di ConcreteCreator sottostanti. Risulta perciò irrinunciabile una buona pianificazione sulla base del tipo di prodotto desiderato.
Vantaggi | Svantaggi |
---|---|
Estensione modulare dell’applicazione | Elevato numero di classi richieste |
Buona testabilità | L’ampliamento dell’applicazione è molto impegnativo |
Possibilità di assegnare nomi significativi ai metodi |
Dove viene impiegato il factory method pattern?
Il factory pattern trova applicazione in diversi contesti, in modo particolare, nei software in cui i prodotti concreti da creare non sono conosciuti o ben definiti in precedenza, il suo approccio alternativo per la gestione delle sottoclassi risulta particolarmente vantaggioso. Alcuni esempi classici di utilizzo sono i framework o le librerie di classe, diventate l’architettura di base pressoché irrinunciabile per lo sviluppo delle moderne applicazioni.
Anche i sistemi di autenticazione godono dei vantaggi offerti dai factory design pattern. Al posto di una classe centrale con diversi parametri che variano a seconda dei permessi utente, con i factory pattern si può delegare il processo di autenticazione alle classi factory, capaci di prendere decisioni autonomamente sulla gestione dei vari utenti.
Inoltre, un design dotato dell’approccio factory pattern è generalmente adatto a qualsiasi software nel quale vengono aggiunte regolarmente nuove classi secondo pianificazione. Soprattutto se queste classi devono eseguire lo stesso processo di creazione.
Factory pattern: esempio in PHP
Il factory pattern può essere utilizzato in applicazioni con i linguaggi di programmazione più diversi. Tra i più comuni si annoverano Java, JavaScript, C++, C#, Python e PHP. L’ultimo di questi linguaggi di scripting è quello usato nell’esempio riportato qui sotto, per il quale ci siamo ispirati a un articolo tedesco del blog Phpmonkeys.
Lo scenario prevede l’utilizzo di una classe astratta “Car” (Creator) e la classe factory “CarFactory” (ConcreteCreator). La prima è progettata nella maniera più semplice possibile e contiene solamente il codice utile a determinare il colore dell’auto (colore standard: bianco) e a eseguirlo:
class Car {
private $color = null;
public function __construct() {
$this->color = "white";
}
public function setColor($color) {
$this->color = $color;
}
public function getColor() {
return $this->color;
}
}
Nella classe factory vengono invece introdotti i metodi necessari per automobili di colore rosso e blu, così come anche un metodo privato per la creazione delle classi vere e proprie:
class CarFactory {
private function __construct() {
}
public static function getBlueCar() {
return self::getCar("blue");
}
public static function getRedCar() {
return self::getCar("red");
}
private static function getCar($color) {
$car = new Car();
$car->setColor($color);
return $car;
}
}
Grazie al factory method pattern si può inoltre scegliere di ampliare questa classe factory di facile lettura aggiungendo le caratteristiche più svariate, come altri colori, il marchio o il prezzo dell’auto.