|
|
|
![]() |
|
Strumenti |
![]() |
#1 |
Member
Iscritto dal: Jun 2006
Messaggi: 117
|
[OOP] Factory Method Design Pattern
Salve,
non riesco a capire bene questo semplice (apparentemente) design pattern. Fondamentalmente quello che io ho capito è che si usa per creare un unico punto centralizzato per la creazione di più oggetti simili fra loro, ma che al momento della creazione non si sa bene quale sia necessario creare. Gli esempi si sprecano. Ma un caso di utilizzo stupido e abbastanza chiaro è il seguente. Voglio poter creare in un'applicazione varie pizze. A runtime però non so quale pizza potrei dover creare (in base a delle ordinazioni per esempio). Quindi l'idea è creare una classe superiore che rappresenti il concetti di "pizza", classe estesa poi da tante "pizze concrete". A questo punto mi serve centralizzare la creazione delle pizze con una "factory". Ma io qui mi perdo... Come si implementa questa situazione con il Factory Method? Da molti schemi (uml e non) trovati sul web sembra che gli elementi di questo design pattern applicati a questo preciso esempio dovrebbero essere: - una classe AbstractFactoryPizza - tante ConcreteFactoryPizza ciascuna per ogni tipo di pizza che si può creare (FunghiFactoryPizza, SalameFactoyPizza, MargheritaFactoryPizza...). Ognua di queste classe ovviamente estende la classe astratta AbstractFactoryPizza - una interfaccia che rappresenti una generica pizza, esempio IPizza - tante classi che implementano la suddetta inferaccia ciascuna per ogni pizza creabile (quindi parallelamente a prima ci sarebbero FunghiPizza, SalamePizza, MargheritaPizza...) Io arrivo a capire fino a qui. Però non capisco come questi elementi interagiscano. Non capisco per esempio come potrei fare da un ipotetico main() a chiamare la creazione di una precisa pizza. Teoricamente credo che debba passare tramite l'AbstractFactoryPizza e in qualche modo decidere quale delle "factory concrete" chiamare. Poi queste istanzieranno in qualche modo l'opportuna pizza ok... però come faccio ad usare AbstractFactoryPizza? E' astratta! Che confusione! Aiuto! |
![]() |
![]() |
![]() |
#2 |
Senior Member
Iscritto dal: May 2004
Città: Londra (Torino)
Messaggi: 3692
|
In un pezzo di codice scriverai
Codice:
AbstractFactoryPizza ThisFactory = new PizzaMargheritaFactory(); P.es. Codice:
AbstractFactoryPizza ThisFactory = new PizzaMargheritaFactory(); AbstractPizza = ThisFactory.Creaecuoci(); .... public abstract class AbstractFactoryPizza { public AbstractPizza Creaecuoci() { AbstractPizza thisPizza= GiveMeInstance(); thisPizza.AggiungiSale(); thisPizza.Appiattisci(); thisPizza.Condisci(); thisPizza.Cuoci(); return thisPizza; } // questo e' un nodo chiave, ovvero il produttore della Factory // Ogni implementazione dovra' ritornare l'oggetto del tipo giusto // Grazie all'inheritance e' possibile, vedi dopo // E' il metodo che lega il livello della singola Factory con // Il livello della classe da produrre abstract public AbstractPizza GiveMeInstance(); } publci abstract class AbstractPizza { public void AggiungiSale() { //Qui scrivo direttamente il codice per aggiungere il sale // su una qualsiasi pizza // Dato che su tutte le pizze il sale si aggiunge nello stesso modo } // Qui invece non scrivo il codice per condire la pizza, dato // che ogni pizza si condisce in modo diverso // con abstract obbligo la singola implementazione di pizza a // scrivere il codice di come verra' condita public abstract void Condisci(); } public class MargheritaFactory:AbstractPizzaFactory { // Metodo chiave, lega il livello di intheritance dei factory // con quello degli oggetti creati public override AbstractPizza GiveMeInstance() { return new PizzaMargherita(); // Possibile, una PizzaMargherita e' una AbstractPizza } }
__________________
Se pensi che il tuo codice sia troppo complesso da capire senza commenti, e' segno che molto probabilmente il tuo codice e' semplicemente mal scritto. E se pensi di avere bisogno di un nuovo commento, significa che ti manca almeno un test. Ultima modifica di gugoXX : 18-12-2008 alle 12:57. |
![]() |
![]() |
![]() |
#3 |
Member
Iscritto dal: Jun 2006
Messaggi: 117
|
Grazie per la risposta molto esaustiva...
Ci ho riflettuto un po' e diciamo che le cose ora mi sono più chiare per certi versi. Ma non riesco a capire bene come un eventuale client (una classe qualunque, un Main per esempio) possa usare questo pattern. Nel codice che mi hai indicato per esempio, un eventuale client deve direttamente creare un'istanza di una precisa factory. Infatti nel tuo esempio il client chiama appunto: AbstractFactoryPizza ThisFactory = new PizzaMargheritaFactory(); Pero' il fatto e' che questo pattern per quanto ho capito, deve prevenire situazioni simili in cui il client deve sapere esattamente che cosa creare. Quindi diciamo che da qualche parte ci deve essere un metodo unico in cui inserire la scelta centralizzata di che tipo di pizza creare in base a qualche parametro. E il client, sapendo solo questo parametro, chiamerà questo metodo ottenendo quindi una generica IPizza oportunamente istanziata su una pizza concreta che pero' il client non sa assolutamente quale sia. Usera' i metodi dell'interfaccia appunto in maniera trasparente. Il fatto da chiarire è dove mettere questo metodo unico centralizzato che effettua la scelta di quale Factory richiamare demandando a quella la creazione dell'opportuna pizza. Idealmente mi viene da pensare che questo metodo deve stare nell'AbstractFactoryPizza, così il client in ogni caso deve fare riferimento solo a quella. Però quella è una classe astratta e quindi non può essere istanziata... sono di nuovo confuso. |
![]() |
![]() |
![]() |
#4 |
Senior Member
Iscritto dal: May 2004
Città: Londra (Torino)
Messaggi: 3692
|
Per fare quello che dici si possono usare i metodi statici.
Per purezza di codice io pero' non legherei la decisione di quale factory restituire all'interno della classe astratta delle factory, quanto piu' aggiungerei una classe Helper statica, dotata (almeno) di un unico metodo statico che restituisce la classe factory sulla base di qualche input Codice:
public static class AbstractFactoryPizzaHelper { public AbstractFactoryPizza GetFactoryPizza(string input) { switch (input) { case "Margherita": return new MargheritaFactory(); break; case "Napoli": return new NapoliFactory(); break; //etc. } } } // Richiamabile con AbstractFactoryPizza myFactoryPizza = AbstractFactoryPizzaHelper.GetFactoryPizza("Margherita"); AbstractPizza myPizza = myFactoryPizza.CreaECouci(); Nulla che non si possa fare (a quel punto sono solo nomi, non sono istanze), ma la soluzione con l'Helper mi piace di piu'
__________________
Se pensi che il tuo codice sia troppo complesso da capire senza commenti, e' segno che molto probabilmente il tuo codice e' semplicemente mal scritto. E se pensi di avere bisogno di un nuovo commento, significa che ti manca almeno un test. Ultima modifica di gugoXX : 19-12-2008 alle 11:42. |
![]() |
![]() |
![]() |
#5 |
Senior Member
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
|
Io ti linko questo thread, magari ti è utile.
__________________
As long as you are basically literate in programming, you should be able to express any logical relationship you understand. If you don’t understand a logical relationship, you can use the attempt to program it as a means to learn about it. (Chris Crawford) |
![]() |
![]() |
![]() |
#6 | ||
Member
Iscritto dal: Jun 2006
Messaggi: 117
|
Quote:
Comunque forse avete inteso male il mio post. La modellazione che ho proposto era solo una mia idea partorita da quello che avevo capito di questo pattern. Però se qualcuno di voi pensa che il pattern in questo caso debba essere utilizzato diversamente me lo dica! Quote:
Pero' in generale una classe client come questa non dovrebbe trovarsi nella condizione di sapere esattamente qualche factory chiamare no?! Altrimenti scusate allora avrebbe chiamato direttamente i costruttori delle due classi Subclass1 e Subclass2 no?! Nel mio esempio quindi un ipotetico client Cameriere prende l'ordinazione e riferisce al DirettoreDellaCucina i nomi delle pizze da creare. Ma non deve sapere esattamente ogni volta quali sono queste pizze. L'ordinazione può essere di 1 pizza come di 100, il cameriere prende nota solo del nome che è come se fosse un ID. Il DirettoreDellaCucina in sostanza dovrebbe essere una sorta di AbstractFactory che poi andrà dal cuoco opportuno (è una pizzeria di lusso!!) e gli dirà di cucinare la pizza per cui lui è specializzato. In questa visione quindi il client è il Cameriere che prende le ordinazioni e deve rivolgersi al DirettoreDellaCucina per ottenere la pizza da consegnare al tavolo. Per rivolgersi al DirettoreDellaCucina però deve usare appunto un metodo unico che dovrebbe essere una sorta di "dammiQuestaPizza(IDpizza)". Ma questo metodo unico dove sta? Teoricamente deve stare in una classe che abbia la visione completa di tutte le factory di pizze esistenti, creando un unico punto centralizzato per la creazione. E a me pare quindi che deve stare dentro DirettoreDellaCucina no?! Pero' si ritorna al discorso del metodo statico... Sto andando a tentoni eh!? Ultima modifica di mcaisco : 19-12-2008 alle 12:18. |
||
![]() |
![]() |
![]() |
#7 |
Senior Member
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
|
No, va bene: stai ragionando, il che è ottimo, direi.
Aspettiamo di sentire GugoXX o qualcun'altro che ha già usato questo pattern nella pratica.
__________________
As long as you are basically literate in programming, you should be able to express any logical relationship you understand. If you don’t understand a logical relationship, you can use the attempt to program it as a means to learn about it. (Chris Crawford) |
![]() |
![]() |
![]() |
#8 |
Senior Member
Iscritto dal: Oct 2005
Messaggi: 3306
|
Io farei qualcosa del genere (in C#):
Codice:
class PizzaFactory { public PizzaFactory() { InitPizzaMaker(); } public IPizza MakePizza(string pizzaName) { return pizzaMaker[pizzaName.ToLower()]; } private void InitPizzaMaker() { pizzaMaker ["margherita"] = PizzaMaker<Margherita>; pizzaMaker ["napoli"] = PizzaMaker<Napoli>; //... } T PizzaMaker<T>() where T : IPizza, new() { return new T(); } private delegate IPizza MakePizza(); private Dictionary<string, MakePizza> pizzaMaker = new Dictionary<string, MakePizza>(); } Ultima modifica di tomminno : 19-12-2008 alle 13:10. |
![]() |
![]() |
![]() |
#9 | |
Senior Member
Iscritto dal: Dec 2005
Città: Istanbul
Messaggi: 1817
|
Quote:
![]() http://discuss.joelonsoftware.com/de...el.3.219431.12
__________________
One of the conclusions that we reached was that the "object" need not be a primitive notion in a programming language; one can build objects and their behaviour from little more than assignable value cells and good old lambda expressions. —Guy Steele |
|
![]() |
![]() |
![]() |
#10 | |
Senior Member
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
|
Quote:
![]() I Design Pattern non sono legge, sono molto comodi e utili, ma non bisogna farsi prendere la mano ![]()
__________________
As long as you are basically literate in programming, you should be able to express any logical relationship you understand. If you don’t understand a logical relationship, you can use the attempt to program it as a means to learn about it. (Chris Crawford) |
|
![]() |
![]() |
![]() |
#11 |
Member
Iscritto dal: Jun 2006
Messaggi: 117
|
Se ho capito l'idea ti Tomminno, allora è un'approccio che ho già visto in qualche esempio. Sostanzialmente l'idea credo sia quella di dichiarare nelle Factory più generale un campo che rappresenti l'insieme delle Factory specializzate esistenti (nel caso viene implementato in C# con un Dictionary, ma può essere un qualunque array, arraylist, hashmap...).
Però credo che il punto centrale sia la dichiarazione della classe Factory generale come classe concreta e non come classe astratta o interfaccia, come credo sia indicato dalle linee guida per questo pattern. Il fatto è che la factory generale deve fungere da factory generale appunto! Cioè deve contenere tutti i metodi che devono essere comunque reimplementati nelle factory concrete derivate da questa. Se non si dichiara la factory generale come astratta o interfaccia, le factory derivate credo siano svincolate da questo dovere di implemtare i metodi specificati come astratti. O no? Altrimenti in effetti che utilità avrebbe usare una classe astratta/interfaccia per la factory generale? |
![]() |
![]() |
![]() |
#12 | |
Senior Member
Iscritto dal: Oct 2005
Messaggi: 3306
|
Quote:
L'Abstract Factory lo usi in un caso del genere (sempre prendendo spunto dalla pizza): Codice:
List<AbstractFactory> abstractFactory = new List<AbstractFactory>(); abstractFactory.Add(FactoryPizza); abstractFactory.Add(FactoryStarter); abstractFactory.Add(FactoryDessert); Dinner dinner = new Dinner(); dinner.Cook(abstractFactory); Ognuno di questi factory sarà internamente implementato come singolo Factory Method. |
|
![]() |
![]() |
![]() |
#13 |
Senior Member
Iscritto dal: Apr 2005
Città: Resana - TV
Messaggi: 960
|
io l'ho reiterpretato così:
Codice:
public interface IIdentificabile { Int32 ID { get; set; } } public static class FabbricaGenerica<T>() where T:IIdentificabile { private LinkedList<T> istanze = new LinkedList<T>(); public static T CreaIstanza(Int32 id) { T istanza = Activator.CreateInstance<T>(); istanza.ID = id; FabbricaGenerica<T>.istanze.AddLast(istanza); return istanza; } private static T CercaIstanza(Int32 id) { // Implementare l'algoritmo desiderato per cercare all'interno della LinkedList } public static T RecuperaIstanza(Int32 id) { T istanza = null; istanza = FabbricaGenerica<T>.CercaIstanza(Int32 id); if (istanza == null) { istanza = FabbricaGenerica<T>.CreaIstanza(id); } return istanza; } } |
![]() |
![]() |
![]() |
#14 |
Senior Member
Iscritto dal: May 2004
Città: Londra (Torino)
Messaggi: 3692
|
Consiglio per questo problema di non costruire un nuovo Generic, dato che in teoria se ne potrebbe fare a meno, sfruttando la sola ereditarieta' e polimorfismo.
__________________
Se pensi che il tuo codice sia troppo complesso da capire senza commenti, e' segno che molto probabilmente il tuo codice e' semplicemente mal scritto. E se pensi di avere bisogno di un nuovo commento, significa che ti manca almeno un test. |
![]() |
![]() |
![]() |
#15 |
Member
Iscritto dal: Jun 2006
Messaggi: 117
|
Beh comunque vorrei chiarire che il problema delle pizze era solo per introdurre la questione e avere qualcosa su cui lavorare.
Il mio intento è capire come funzionano i pattern factory (method e abstract). Se avete un esempio più calzante che chiarisca bene come funzionano questi pattern siete caldamente invitati a discuterne!!! |
![]() |
![]() |
![]() |
Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 12:00.