Factory method: differenze tra le versioni

Contenuto cancellato Contenuto aggiunto
Alecs.bot (discussione | contributi)
Botcrux (discussione | contributi)
m Bot: fix wl, replaced: Design Patterns patterns → Design pattern
 
(48 versioni intermedie di 35 utenti non mostrate)
Riga 1:
[[File:FactoryMethod.svg|thumb|upright=1.4|Schema logico del Factory method in [[Unified Modeling Language|UML]]]]
{{S|informatica}}
[[ImmagineFile:FactoryMethodFactory Method pattern in LePUS3.svgpng|thumb|right|300pxupright=1.4|Schema logico del Factory method in [[LePUS3]]]]
Nella [[Programmazione_orientata_agli_oggetti|programmazione ad oggetti]], il '''Factory Method''' è uno dei design pattern fondamentali, definiti originariamente dalla [[gang of four]].
 
ComeNella altri[[Programmazione creationalorientata patternagli oggetti|programmazione ad oggetti]], fornisceil un'''factory metodomethod''' è uno dei [[design pattern]] fondamentali per istanziarel'implementazione undel oggettoconcetto senzadi sapere[[factory a(informatica)|factory]]. prioriCome laaltri sua[[pattern creazionali]], esso indirizza il problema della creazione di [[oggetto (informatica)|oggetti]] senza specificarne l'esatta [[classe (informatica)|classe]]. Questo pattern raggiunge il suo scopo fornendo un'[[interfaccia_interfaccia (informatica)|interfaccia]] per creare un oggetto, ma lascia che le sottoclassi decidano quale oggetto istanziare.<ref>[[Design Patterns|Gang Of Four]]</ref>
 
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 (informatica)|astrazione]]. Il factory method indirizza questi problemi definendo un [[metodo (informatica)|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.
==Struttura di un Factory Method==
 
== Applicabilità ==
* '''Product''': definisce l'interfaccia dell'oggetto creato dal factory method.
Il pattern factory può essere utilizzato quando:
* '''ConcreteProduct''': implementa l'interfaccia di Product.
* La creazione di un oggetto preclude il suo riuso senza una significativa duplicazione di codice.
* '''Creator''': dichiara il factory method che ritorna un oggetto di tipo Product e lo può chiamare per creare un oggetto di tipo Product; il creator può definire un'implementazione del factory method che ritorna un oggetto ConcreteProduct di default.
* La creazione di un oggetto richiede l'accesso ad informazioni o risorse che non dovrebbero essere contenute nella classe di composizione.
* '''ConcreteCreator''': ridefinisce il factory method per tornare un'istanza di un ConcreteProduct
* La gestione del ciclo di vita degli oggetti gestiti deve essere centralizzata in modo da assicurare un comportamento coerente 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.<ref>{{Cita libro
==Funzionamento==
|autore=[[Michael Feathers|Feathers, Michael]]
|isbn=978-0-13-117705-5
|titolo=Working Effectively with Legacy Code
|data=ottobre 2004
|editore=Prentice Hall Professional Technical Reference
|città=Upper Saddle River, NJ
}}</ref> Se una classe <code>pippo</code> crea un oggetto <code>Paperino</code> 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 <code>Paperino</code> è posta nella [[funzione virtuale]] del factory <code>createPaperino</code> nella classe <code>pippo</code>. Per il test, <code>TestPippo</code> (una sottoclasse di <code>pippo</code>) può essere creata, con il metodo <code>createPaperino</code> ridefinito per creare e restituire un oggetto di <code>FakePaperino</code>, un oggetto di mock-up. Gli unit test possono, quindi, usare <code>TestPippo</code> per testare le funzionalità di <code>pippo</code> senza incorrere negli effetti collaterali dati dall'uso dell'oggetto reale <code>Paperino</code>.
 
== Esempi di implementazione ==
Il Creator si affida alle sottoclassi per quanto riguarda la definizione del factory method, cosicché esso ritorni un'istanza appropriata del Product.
=== 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 stanze casuali lungo tutto il labirinto (questo esempio in [[Java (linguaggio di programmazione)|Java]] è simile a quello presente sul libro ''[[Design Patterns]]''). Il gioco regolare può usare un modello del tipo:
 
<syntaxhighlight lang=Java>
Il codice tratta solo con l'interfaccia e con nessuna classe concreta.
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();
}
}
</syntaxhighlight>
 
Nel codice summenzionato, il costruttore <code>MazeGame</code> è un template che fornisce della logica comune e fa riferimento al metodo factory <code>makeRoom</code> 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 <code>makeRoom</code> per l'implementazione delle stanze magiche:
{{Design Patterns Patterns}}
 
<syntaxhighlight lang=Java>
[[Categoria:Pattern]]
public class MagicMazeGame extends MazeGame {
@Override
protected Room makeRoom() {
return new MagicRoom();
}
}
</syntaxhighlight>
 
=== PHP ===
[[de:Fabrikmethode]]
Un esempio in [[PHP]] è qui riportato:
[[en:Factory method pattern]]
<syntaxhighlight lang="php">
[[es:Factory Method (patrón de diseño)]]
// uso di Factory Method design pattern
[[fr:Fabrique (patron de conception)]]
 
[[he:תבנית Factory Method]]
interface FabbricaAutomobile
[[ja:Factory Method パターン]]
{
[[ko:팩토리 메서드 패턴]]
public function costruiscoAuto(auto $auto);
[[nl:Factory (ontwerppatroon)]]
}
[[pl:Metoda wytwórcza (wzorzec projektowy)]]
 
[[pt:Factory Method]]
interface Auto
[[ru:Фабричный метод (шаблон проектирования)]]
{
[[th:แฟกทอรีเมธอดแพตเทิร์น]]
public function costruiscoTelaio();
[[uk:Фабричний метод (шаблон проектування)]]
public function costruiscoMotore();
[[vi:Factory method pattern]]
public function costruiscoInterni();
[[zh:工厂方法]]
public function assembloAuto();
public function testoAuto();
}
 
// uso di Prototype design pattern
 
abstract class PrototipoAuto
{
protected $telaio;
protected $motore;
protected $interni;
protected $assemblato;
protected $test;
abstract function __clone();
abstract function modificaNome($nome);
}
 
class FabbricaModello implements FabbricaAutomobile
{
public function costruiscoAuto(auto $auto)
{
return $auto;
}
}
 
class Fiat extends PrototipoAuto implements Auto
{
private $nome;
public function __construct()
{
$this->telaio = 0;
$this->motore = 0;
$this->interni = 0;
$this->assemblato = 0;
$this->test = 0;
$this->nome = "Fiat";
echo "Costruzione {$this->nome} iniziata\n";
}
function __clone()
{
echo "Costruzione clone {$this->nome} iniziata\n";
}
public function modificaNome($nome)
{
echo "Nome {$this->nome} modificato ";
$this->nome = $nome;
echo "in {$this->nome}\n";
return $this;
}
public function costruiscoTelaio()
{
echo "Telaio {$this->nome} costruito\n";
$this->telaio = 1;
return $this;
}
public function costruiscoMotore()
{
echo "Motore {$this->nome} costruito\n";
$this->motore = 1;
return $this;
}
public function costruiscoInterni()
{
echo "Interni {$this->nome} costruiti\n";
$this->interni = 1;
return $this;
}
public function assembloAuto()
{
echo "{$this->nome} assemblata\n";
$this->assemblato = 1;
return $this;
}
public function testoAuto()
{
if($this->telaio && $this->motore && $this->interni && $this->assemblato)
{
echo "Test {$this->nome} superato\n";
$this->test = 1;
}
else
{
echo "Test {$this->nome} non superato\n";
$this->test = 0;
}
return $this;
}
}
 
class Lancia extends PrototipoAuto implements Auto
{
private $nome;
public function __construct()
{
$this->telaio = 0;
$this->motore = 0;
$this->interni = 0;
$this->assemblato = 0;
$this->test = 0;
$this->nome = "Lancia";
echo "Costruzione {$this->nome} iniziata\n";
}
function __clone()
{
}
public function modificaNome($nome)
{
return $this;
}
public function costruiscoTelaio()
{
echo "Telaio {$this->nome} costruito\n";
$this->telaio = 1;
return $this;
}
public function costruiscoMotore()
{
echo "Motore {$this->nome} acquistato\n";
$this->motore = 1;
return $this;
}
public function costruiscoInterni()
{
echo "Interni {$this->nome} costruiti\n";
$this->interni = 1;
return $this;
}
public function assembloAuto()
{
echo "{$this->nome} assemblata\n";
$this->assemblato = 1;
return $this;
}
public function testoAuto()
{
if($this->telaio && $this->motore && $this->interni && $this->assemblato)
{
echo "Test {$this->nome} superato\n";
$this->test = 1;
}
else
{
echo "Test {$this->nome} non superato\n";
$this->test = 0;
}
return $this;
}
}
 
class NuovaAuto
{
private $fabbricaModello;
 
public function __construct()
{
$this->fabbricaModello = new FabbricaModello();
echo "\n--Nuova Fiat usando Factory Method--\n";
$fiat = new Fiat();
$this->fabbricaModello->costruiscoAuto($fiat)->costruiscoTelaio()->costruiscoMotore()->costruiscoInterni()->assembloAuto()->testoAuto();
echo "\n--Nuova Lancia usando Factory Method--\n";
$this->fabbricaModello->costruiscoAuto(new Lancia())->costruiscoTelaio()->costruiscoMotore()->costruiscoInterni()->testoAuto()->assembloAuto()->testoAuto();
echo "\n--Nuova Bianchi usando Factory Method e Prototype--\n";
// usando prototype si crea un clone dell'oggetto Fiat, infatti si può eseguire "testAuto" subito dopo "modificaNome"
$this->fabbricaModello->costruiscoAuto(clone $fiat)->modificaNome("Bianchi")->testoAuto();
}
}
 
$nuoveAuto = new NuovaAuto();
 
//Risultato: #quanton81
 
//--Nuova Fiat usando Factory Method--
//Costruzione Fiat iniziata
//Telaio Fiat costruito
//Motore Fiat costruito
//Interni Fiat costruiti
//Fiat assemblata
//Test Fiat superato
//
//--Nuova Lancia usando Factory Method--
//Costruzione Lancia iniziata
//Telaio Lancia costruito
//Motore Lancia acquistato
//Interni Lancia costruiti
//Test Lancia non superato
//Lancia assemblata
//Test Lancia superato
//
//--Nuova Bianchi usando Factory Method e Prototype--
//Costruzione clone Fiat iniziata
//Nome Fiat modificato in Bianchi
//Test Bianchi superato
</syntaxhighlight>
 
== 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 perché 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 ==
<references/>
 
== Voci correlate ==
* [[Design pattern]]
 
== Altri progetti ==
{{interprogetto|commons=Factory method}}
 
{{Design pattern}}
{{Portale|informatica}}
 
[[Categoria:Design pattern]]