Factory method
Nella programmazione ad oggetti, il Factory Method è uno dei design pattern fondamentali per l'implementazione del concetto di factories. Come altri pattern creazionali, esso indirizza il problema della creazione di oggetti senza specificarne l'esatta classe. Questo pattern raggiunge il suo scopo fornendo un'interfaccia per creare un oggetto, ma lascia che le sottoclassi decidano quale oggetto istanziare.[1]


La creazione di un oggetto può, spesso, richiedere processi complessi la cui collocazione all'interno della classe di composizione potrebbe non essere appropriata. Esso può, inoltre, comportare la duplicazione di codice, richiedere informazioni non accessibili alla classe di composizione, o non provvedere un sufficiente livello di astrazione. Il factory method indirizza questi problemi definendo un metodo separato per la creazione degli oggetti; tale metodo può essere ridefinito dalle sottoclassi per definire il tipo derivato di prodotto che verrà effettivamente creato.
Applicabilità
Il pattern factory può essere utilizzato quando:
- La creazione di un oggetto preclude il suo riuso senza una significativa duplicazione di codice.
- La creazione di un oggetto richiede l'accesso ad informazioni o risorse che non dovrebbero essere contenuto nella classe di composizione.
- La gestione del ciclo di vita degli oggetti gestiti deve essere centralizzata in modo da assicurare un comportamento consistente all'interno dell'applicazione.
I metodi di factory sono spesso utilizzati in toolkit e framework, dove il codice delle librerie necessita di poter creare oggetti il cui tipo è implementato nelle sottoclassi delle applicazioni che utilizzano il framework. Essi sono, inoltre, utilizzati nel Test Driven Development per porre le classi sotto test.[2] Se una classe Pippo
crea un oggetto Pluto
che non può essere posto sotto una suite automatica di unit testing (ad esempio perché non può accedere ad una risorsa di produzione cui ha bisogno, come una base dati), allora la creazione dell'oggetto Paperino
è posta nella funzione virtuale del factory createPaperino
nella class Pippo
. Per il test, TestPippo
(una sottoclasse di Pippo
) può essere creata, con il metodo createPaperino
ridefinito per creare e ritornare un oggetto di FakePaperino
, un oggetto di mock-up. Gli unit test possono, quindi, usare TestPippo
per testare le funzionalità di Pippo
senza incorrere negli effetti collaterali dati dall'uso dell'oggetto reale Paperino
.
Esempi di implementazione
Java
Un gioco del labirinto può essere giocato in due modi, uno con stanze regolari che sono connesse solo a quelle adiacenti e uno con stanze magiche che possono trasportare i giocatori in stanza casuali lungo di tutto il labirinto (questo esempio in Java è simile a quello presente sul libro Design Patterns). Il gioco regolare può usare un modello del tipo:
public class MazeGame {
public MazeGame() {
Room room1 = makeRoom();
Room room2 = makeRoom();
room1.connect(room2);
this.addRoom(room1);
this.addRoom(room2);
}
protected Room makeRoom() {
return new OrdinaryRoom();
}
}
Nel codice summenzionato, il costruttore MazeGame
è un template che fornisce della logica comune e fa riferimento al metodo factory makeRoom
che incapsula la logica di creazione delle stanza in modo che le stanze possano essere utilizzate nelle sottoclassi. Per implementare l'altra modalità di gioco, infatti, è necessario solo ridefinire makeRoom
per l'implementazione delle stanze magiche:
public class MagicMazeGame extends MazeGame {
@Override
protected Room makeRoom() {
return new MagicRoom();
}
}
PHP
Un esempio in PHP è qui riportato:
class Factory
{
public static function build($type)
{
$class = 'Format' . $type;
if (!class_exists($class)) {
throw new Exception('Missing format class.');
}
return new $class;
}
}
class FormatString {}
class FormatNumber {}
try {
$string = Factory::build('String');
}
catch (Exception $e) {
echo $e->getMessage();
}
try {
$number = Factory::build('Number');
}
catch (Exception $e) {
echo $e->getMessage();
}
Limitazioni
Ci sono tre limitazioni, all'uso di questo metodo. Il primo è relativo al refactoring, gli altri due sono relativi all'estensione di classi.
- La prima limitazione consiste nel fatto che il refactoring di una classe esistente causa il malfunzionamento dei client esistenti.
- La seconda limitazione è dovuta al fatto che, poiché il pattern si basa sull'uso di costruttori privati, la classe non può essere estesa. Ogni sottoclasse dovrebbe invocare il costruttore ereditato, ma non può farlo nella misura in cui quest'ultimo è privato.
- La terza limitazione è dovuta al fatto che, se estendiamo una classe (ad esempio rendendo il costruttore 'protected'; rischioso ma possibile), le sottoclassi devono precedere la reimplementazione di tutti i metodi di factory con la stessa dichiarazione. La possibilità, fornita da alcuni linguaggi, di usare le classi reflection può, tuttavia, risolvere questo problema.
Note
- ^ Gang Of Four
- ^ Feathers, Michael, Working Effectively with Legacy Code, Upper Saddle River, NJ, Prentice Hall Professional Technical Reference, October 2004, ISBN 978-0-13-117705-5.
Voci correlate
- Design Patterns, il libro della gang of four che ha introdotto il pattern
- Design pattern, introduzione generale ai design pattern
Altri progetti
- Wikimedia Commons contiene immagini o altri file su factory method